]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | #include "sd-bus.h" | |
4 | ||
5 | #include "alloc-util.h" | |
6 | #include "bus-common-errors.h" | |
7 | #include "bus-get-properties.h" | |
8 | #include "bus-label.h" | |
9 | #include "bus-object.h" | |
10 | #include "bus-polkit.h" | |
11 | #include "bus-util.h" | |
12 | #include "copy.h" | |
13 | #include "env-util.h" | |
14 | #include "errno-util.h" | |
15 | #include "fd-util.h" | |
16 | #include "hashmap.h" | |
17 | #include "in-addr-util.h" | |
18 | #include "local-addresses.h" | |
19 | #include "machine.h" | |
20 | #include "machine-dbus.h" | |
21 | #include "machined.h" | |
22 | #include "mount-util.h" | |
23 | #include "operation.h" | |
24 | #include "path-util.h" | |
25 | #include "signal-util.h" | |
26 | #include "string-util.h" | |
27 | #include "strv.h" | |
28 | ||
29 | static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass); | |
30 | static BUS_DEFINE_PROPERTY_GET2(property_get_state, "s", Machine, machine_get_state, machine_state_to_string); | |
31 | ||
32 | static int property_get_netif( | |
33 | sd_bus *bus, | |
34 | const char *path, | |
35 | const char *interface, | |
36 | const char *property, | |
37 | sd_bus_message *reply, | |
38 | void *userdata, | |
39 | sd_bus_error *error) { | |
40 | ||
41 | Machine *m = ASSERT_PTR(userdata); | |
42 | ||
43 | assert(bus); | |
44 | assert(reply); | |
45 | ||
46 | assert_cc(sizeof(int) == sizeof(int32_t)); | |
47 | ||
48 | return sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int)); | |
49 | } | |
50 | ||
51 | int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
52 | Machine *m = ASSERT_PTR(userdata); | |
53 | int r; | |
54 | ||
55 | assert(message); | |
56 | ||
57 | const char *details[] = { | |
58 | "machine", m->name, | |
59 | "verb", "unregister", | |
60 | NULL | |
61 | }; | |
62 | ||
63 | r = bus_verify_polkit_async( | |
64 | message, | |
65 | "org.freedesktop.machine1.manage-machines", | |
66 | details, | |
67 | &m->manager->polkit_registry, | |
68 | error); | |
69 | if (r < 0) | |
70 | return r; | |
71 | if (r == 0) | |
72 | return 1; /* Will call us back */ | |
73 | ||
74 | r = machine_finalize(m); | |
75 | if (r < 0) | |
76 | return r; | |
77 | ||
78 | return sd_bus_reply_method_return(message, NULL); | |
79 | } | |
80 | ||
81 | int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
82 | Machine *m = ASSERT_PTR(userdata); | |
83 | int r; | |
84 | ||
85 | assert(message); | |
86 | ||
87 | const char *details[] = { | |
88 | "machine", m->name, | |
89 | "verb", "terminate", | |
90 | NULL | |
91 | }; | |
92 | ||
93 | r = bus_verify_polkit_async( | |
94 | message, | |
95 | "org.freedesktop.machine1.manage-machines", | |
96 | details, | |
97 | &m->manager->polkit_registry, | |
98 | error); | |
99 | if (r < 0) | |
100 | return r; | |
101 | if (r == 0) | |
102 | return 1; /* Will call us back */ | |
103 | ||
104 | r = machine_stop(m); | |
105 | if (r < 0) | |
106 | return r; | |
107 | ||
108 | return sd_bus_reply_method_return(message, NULL); | |
109 | } | |
110 | ||
111 | int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
112 | Machine *m = ASSERT_PTR(userdata); | |
113 | const char *swho; | |
114 | int32_t signo; | |
115 | KillWhom whom; | |
116 | int r; | |
117 | ||
118 | assert(message); | |
119 | ||
120 | r = sd_bus_message_read(message, "si", &swho, &signo); | |
121 | if (r < 0) | |
122 | return r; | |
123 | ||
124 | if (isempty(swho)) | |
125 | whom = KILL_ALL; | |
126 | else { | |
127 | whom = kill_whom_from_string(swho); | |
128 | if (whom < 0) | |
129 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); | |
130 | } | |
131 | ||
132 | if (!SIGNAL_VALID(signo)) | |
133 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); | |
134 | ||
135 | const char *details[] = { | |
136 | "machine", m->name, | |
137 | "verb", "kill", | |
138 | NULL | |
139 | }; | |
140 | ||
141 | r = bus_verify_polkit_async( | |
142 | message, | |
143 | "org.freedesktop.machine1.manage-machines", | |
144 | details, | |
145 | &m->manager->polkit_registry, | |
146 | error); | |
147 | if (r < 0) | |
148 | return r; | |
149 | if (r == 0) | |
150 | return 1; /* Will call us back */ | |
151 | ||
152 | r = machine_kill(m, whom, signo); | |
153 | if (r < 0) | |
154 | return r; | |
155 | ||
156 | return sd_bus_reply_method_return(message, NULL); | |
157 | } | |
158 | ||
159 | int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
160 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
161 | _cleanup_free_ struct local_address *addresses = NULL; | |
162 | Machine *m = ASSERT_PTR(userdata); | |
163 | int r; | |
164 | ||
165 | assert(message); | |
166 | ||
167 | r = sd_bus_message_new_method_return(message, &reply); | |
168 | if (r < 0) | |
169 | return r; | |
170 | ||
171 | r = sd_bus_message_open_container(reply, 'a', "(iay)"); | |
172 | if (r < 0) | |
173 | return r; | |
174 | ||
175 | int n = machine_get_addresses(m, &addresses); | |
176 | if (n == -ENONET) | |
177 | return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); | |
178 | if (ERRNO_IS_NEG_NOT_SUPPORTED(n)) | |
179 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); | |
180 | if (n < 0) | |
181 | return sd_bus_error_set_errnof(error, n, "Failed to get addresses: %m"); | |
182 | ||
183 | for (int i = 0; i < n; i++) { | |
184 | r = sd_bus_message_open_container(reply, 'r', "iay"); | |
185 | if (r < 0) | |
186 | return r; | |
187 | ||
188 | r = sd_bus_message_append(reply, "i", addresses[i].family); | |
189 | if (r < 0) | |
190 | return r; | |
191 | ||
192 | r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family)); | |
193 | if (r < 0) | |
194 | return r; | |
195 | ||
196 | r = sd_bus_message_close_container(reply); | |
197 | if (r < 0) | |
198 | return r; | |
199 | } | |
200 | ||
201 | r = sd_bus_message_close_container(reply); | |
202 | if (r < 0) | |
203 | return r; | |
204 | ||
205 | return sd_bus_message_send(reply); | |
206 | } | |
207 | ||
208 | int bus_machine_method_get_ssh_info(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
209 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
210 | Machine *m = ASSERT_PTR(userdata); | |
211 | int r; | |
212 | ||
213 | assert(message); | |
214 | ||
215 | r = sd_bus_message_new_method_return(message, &reply); | |
216 | if (r < 0) | |
217 | return r; | |
218 | ||
219 | if (!m->ssh_address || !m->ssh_private_key_path) | |
220 | return -ENOENT; | |
221 | ||
222 | r = sd_bus_message_append(reply, "ss", m->ssh_address, m->ssh_private_key_path); | |
223 | if (r < 0) | |
224 | return r; | |
225 | ||
226 | return sd_bus_message_send(reply); | |
227 | } | |
228 | ||
229 | int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
230 | _cleanup_strv_free_ char **l = NULL; | |
231 | Machine *m = ASSERT_PTR(userdata); | |
232 | int r; | |
233 | ||
234 | assert(message); | |
235 | ||
236 | r = machine_get_os_release(m, &l); | |
237 | if (r == -ENONET) | |
238 | return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information."); | |
239 | if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) | |
240 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); | |
241 | if (r < 0) | |
242 | return sd_bus_error_set_errnof(error, r, "Failed to get OS release: %m"); | |
243 | ||
244 | return bus_reply_pair_array(message, l); | |
245 | } | |
246 | ||
247 | int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
248 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
249 | _cleanup_free_ char *pty_name = NULL; | |
250 | _cleanup_close_ int master = -EBADF; | |
251 | Machine *m = ASSERT_PTR(userdata); | |
252 | int r; | |
253 | ||
254 | assert(message); | |
255 | ||
256 | const char *details[] = { | |
257 | "machine", m->name, | |
258 | NULL | |
259 | }; | |
260 | ||
261 | r = bus_verify_polkit_async( | |
262 | message, | |
263 | m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", | |
264 | details, | |
265 | &m->manager->polkit_registry, | |
266 | error); | |
267 | if (r < 0) | |
268 | return r; | |
269 | if (r == 0) | |
270 | return 1; /* Will call us back */ | |
271 | ||
272 | master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name); | |
273 | if (master < 0) | |
274 | return master; | |
275 | ||
276 | r = sd_bus_message_new_method_return(message, &reply); | |
277 | if (r < 0) | |
278 | return r; | |
279 | ||
280 | r = sd_bus_message_append(reply, "hs", master, pty_name); | |
281 | if (r < 0) | |
282 | return r; | |
283 | ||
284 | return sd_bus_message_send(reply); | |
285 | } | |
286 | ||
287 | int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
288 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
289 | _cleanup_free_ char *pty_name = NULL; | |
290 | _cleanup_close_ int master = -EBADF; | |
291 | Machine *m = ASSERT_PTR(userdata); | |
292 | int r; | |
293 | ||
294 | assert(message); | |
295 | ||
296 | const char *details[] = { | |
297 | "machine", m->name, | |
298 | "verb", "login", | |
299 | NULL | |
300 | }; | |
301 | ||
302 | r = bus_verify_polkit_async( | |
303 | message, | |
304 | m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", | |
305 | details, | |
306 | &m->manager->polkit_registry, | |
307 | error); | |
308 | if (r < 0) | |
309 | return r; | |
310 | if (r == 0) | |
311 | return 1; /* Will call us back */ | |
312 | ||
313 | master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name); | |
314 | if (master < 0) | |
315 | return master; | |
316 | ||
317 | r = machine_start_getty(m, pty_name, error); | |
318 | if (r < 0) | |
319 | return r; | |
320 | ||
321 | r = sd_bus_message_new_method_return(message, &reply); | |
322 | if (r < 0) | |
323 | return r; | |
324 | ||
325 | r = sd_bus_message_append(reply, "hs", master, pty_name); | |
326 | if (r < 0) | |
327 | return r; | |
328 | ||
329 | return sd_bus_message_send(reply); | |
330 | } | |
331 | ||
332 | int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
333 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
334 | _cleanup_free_ char *pty_name = NULL; | |
335 | _cleanup_close_ int master = -EBADF; | |
336 | _cleanup_strv_free_ char **env = NULL, **args_wire = NULL, **args = NULL; | |
337 | _cleanup_free_ char *command_line = NULL; | |
338 | Machine *m = ASSERT_PTR(userdata); | |
339 | const char *user, *path; | |
340 | int r; | |
341 | ||
342 | assert(message); | |
343 | ||
344 | r = sd_bus_message_read(message, "ss", &user, &path); | |
345 | if (r < 0) | |
346 | return r; | |
347 | user = isempty(user) ? "root" : user; | |
348 | r = sd_bus_message_read_strv(message, &args_wire); | |
349 | if (r < 0) | |
350 | return r; | |
351 | if (isempty(path)) { | |
352 | path = machine_default_shell_path(); | |
353 | args = machine_default_shell_args(user); | |
354 | if (!args) | |
355 | return -ENOMEM; | |
356 | } else { | |
357 | if (!path_is_absolute(path)) | |
358 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path); | |
359 | args = TAKE_PTR(args_wire); | |
360 | if (strv_isempty(args)) { | |
361 | args = strv_free(args); | |
362 | ||
363 | args = strv_new(path); | |
364 | if (!args) | |
365 | return -ENOMEM; | |
366 | } | |
367 | } | |
368 | ||
369 | r = sd_bus_message_read_strv(message, &env); | |
370 | if (r < 0) | |
371 | return r; | |
372 | if (!strv_env_is_valid(env)) | |
373 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); | |
374 | ||
375 | command_line = strv_join(args, " "); | |
376 | if (!command_line) | |
377 | return -ENOMEM; | |
378 | const char *details[] = { | |
379 | "machine", m->name, | |
380 | "user", user, | |
381 | "program", path, | |
382 | "command_line", command_line, | |
383 | NULL | |
384 | }; | |
385 | ||
386 | r = bus_verify_polkit_async( | |
387 | message, | |
388 | m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", | |
389 | details, | |
390 | &m->manager->polkit_registry, | |
391 | error); | |
392 | if (r < 0) | |
393 | return r; | |
394 | if (r == 0) | |
395 | return 1; /* Will call us back */ | |
396 | ||
397 | master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name); | |
398 | if (master < 0) | |
399 | return master; | |
400 | ||
401 | r = machine_start_shell(m, master, pty_name, user, path, args, env, error); | |
402 | if (r < 0) | |
403 | return r; | |
404 | ||
405 | r = sd_bus_message_new_method_return(message, &reply); | |
406 | if (r < 0) | |
407 | return r; | |
408 | ||
409 | r = sd_bus_message_append(reply, "hs", master, pty_name); | |
410 | if (r < 0) | |
411 | return r; | |
412 | ||
413 | return sd_bus_message_send(reply); | |
414 | } | |
415 | ||
416 | int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
417 | int read_only, make_file_or_directory; | |
418 | const char *dest, *src, *propagate_directory; | |
419 | Machine *m = ASSERT_PTR(userdata); | |
420 | MountInNamespaceFlags flags = 0; | |
421 | uid_t uid; | |
422 | int r; | |
423 | ||
424 | assert(message); | |
425 | ||
426 | if (m->class != MACHINE_CONTAINER) | |
427 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines."); | |
428 | ||
429 | r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_file_or_directory); | |
430 | if (r < 0) | |
431 | return r; | |
432 | ||
433 | if (!path_is_absolute(src) || !path_is_normalized(src)) | |
434 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized."); | |
435 | ||
436 | if (isempty(dest)) | |
437 | dest = src; | |
438 | else if (!path_is_absolute(dest) || !path_is_normalized(dest)) | |
439 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized."); | |
440 | ||
441 | const char *details[] = { | |
442 | "machine", m->name, | |
443 | "verb", "bind", | |
444 | "src", src, | |
445 | "dest", dest, | |
446 | NULL | |
447 | }; | |
448 | ||
449 | r = bus_verify_polkit_async( | |
450 | message, | |
451 | "org.freedesktop.machine1.manage-machines", | |
452 | details, | |
453 | &m->manager->polkit_registry, | |
454 | error); | |
455 | if (r < 0) | |
456 | return r; | |
457 | if (r == 0) | |
458 | return 1; /* Will call us back */ | |
459 | ||
460 | r = machine_get_uid_shift(m, &uid); | |
461 | if (r < 0) | |
462 | return r; | |
463 | if (uid != 0) | |
464 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied."); | |
465 | ||
466 | if (read_only) | |
467 | flags |= MOUNT_IN_NAMESPACE_READ_ONLY; | |
468 | if (make_file_or_directory) | |
469 | flags |= MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY; | |
470 | ||
471 | propagate_directory = strjoina("/run/systemd/nspawn/propagate/", m->name); | |
472 | r = bind_mount_in_namespace( | |
473 | &m->leader, | |
474 | propagate_directory, | |
475 | "/run/host/incoming/", | |
476 | src, dest, | |
477 | flags); | |
478 | if (r < 0) | |
479 | return sd_bus_error_set_errnof(error, r, "Failed to mount %s on %s in machine's namespace: %m", src, dest); | |
480 | ||
481 | return sd_bus_reply_method_return(message, NULL); | |
482 | } | |
483 | ||
484 | int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
485 | const char *src, *dest, *host_path, *container_path; | |
486 | CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS; | |
487 | Machine *m = ASSERT_PTR(userdata); | |
488 | Manager *manager = m->manager; | |
489 | bool copy_from; | |
490 | int r; | |
491 | ||
492 | assert(message); | |
493 | ||
494 | if (m->manager->n_operations >= OPERATIONS_MAX) | |
495 | return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies."); | |
496 | ||
497 | if (m->class != MACHINE_CONTAINER) | |
498 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines."); | |
499 | ||
500 | r = sd_bus_message_read(message, "ss", &src, &dest); | |
501 | if (r < 0) | |
502 | return r; | |
503 | ||
504 | if (endswith(sd_bus_message_get_member(message), "WithFlags")) { | |
505 | uint64_t raw_flags; | |
506 | ||
507 | r = sd_bus_message_read(message, "t", &raw_flags); | |
508 | if (r < 0) | |
509 | return r; | |
510 | ||
511 | if ((raw_flags & ~_MACHINE_COPY_FLAGS_MASK_PUBLIC) != 0) | |
512 | return -EINVAL; | |
513 | ||
514 | if (raw_flags & MACHINE_COPY_REPLACE) | |
515 | copy_flags |= COPY_REPLACE; | |
516 | } | |
517 | ||
518 | if (!path_is_absolute(src)) | |
519 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute."); | |
520 | ||
521 | if (isempty(dest)) | |
522 | dest = src; | |
523 | else if (!path_is_absolute(dest)) | |
524 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute."); | |
525 | ||
526 | const char *details[] = { | |
527 | "machine", m->name, | |
528 | "verb", "copy", | |
529 | "src", src, | |
530 | "dest", dest, | |
531 | NULL | |
532 | }; | |
533 | ||
534 | r = bus_verify_polkit_async( | |
535 | message, | |
536 | "org.freedesktop.machine1.manage-machines", | |
537 | details, | |
538 | &manager->polkit_registry, | |
539 | error); | |
540 | if (r < 0) | |
541 | return r; | |
542 | if (r == 0) | |
543 | return 1; /* Will call us back */ | |
544 | ||
545 | copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom"); | |
546 | ||
547 | if (copy_from) { | |
548 | container_path = src; | |
549 | host_path = dest; | |
550 | } else { | |
551 | host_path = src; | |
552 | container_path = dest; | |
553 | } | |
554 | ||
555 | Operation *op; | |
556 | r = machine_copy_from_to_operation(manager, m, host_path, container_path, copy_from, copy_flags, &op); | |
557 | if (r < 0) | |
558 | return sd_bus_error_set_errnof(error, r, "Failed to copy from/to machine '%s': %m", m->name); | |
559 | ||
560 | operation_attach_bus_reply(op, message); | |
561 | return 1; | |
562 | } | |
563 | ||
564 | int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
565 | _cleanup_close_ int fd = -EBADF; | |
566 | Machine *m = ASSERT_PTR(userdata); | |
567 | int r; | |
568 | ||
569 | assert(message); | |
570 | ||
571 | const char *details[] = { | |
572 | "machine", m->name, | |
573 | "verb", "open_root_directory", | |
574 | NULL | |
575 | }; | |
576 | ||
577 | r = bus_verify_polkit_async( | |
578 | message, | |
579 | "org.freedesktop.machine1.manage-machines", | |
580 | details, | |
581 | &m->manager->polkit_registry, | |
582 | error); | |
583 | if (r < 0) | |
584 | return r; | |
585 | if (r == 0) | |
586 | return 1; /* Will call us back */ | |
587 | ||
588 | fd = machine_open_root_directory(m); | |
589 | if (ERRNO_IS_NEG_NOT_SUPPORTED(fd)) | |
590 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines."); | |
591 | if (fd < 0) | |
592 | return sd_bus_error_set_errnof(error, fd, "Failed to open root directory of machine '%s': %m", m->name); | |
593 | ||
594 | return sd_bus_reply_method_return(message, "h", fd); | |
595 | } | |
596 | ||
597 | int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
598 | Machine *m = ASSERT_PTR(userdata); | |
599 | uid_t shift = 0; | |
600 | int r; | |
601 | ||
602 | assert(message); | |
603 | ||
604 | /* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but | |
605 | * we kinda have to for this. */ | |
606 | ||
607 | if (m->class == MACHINE_HOST) | |
608 | return sd_bus_reply_method_return(message, "u", UINT32_C(0)); | |
609 | ||
610 | if (m->class != MACHINE_CONTAINER) | |
611 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines."); | |
612 | ||
613 | r = machine_get_uid_shift(m, &shift); | |
614 | if (r == -ENXIO) | |
615 | return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m->name); | |
616 | if (r < 0) | |
617 | return r; | |
618 | ||
619 | return sd_bus_reply_method_return(message, "u", (uint32_t) shift); | |
620 | } | |
621 | ||
622 | static int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { | |
623 | Manager *m = ASSERT_PTR(userdata); | |
624 | Machine *machine; | |
625 | int r; | |
626 | ||
627 | assert(bus); | |
628 | assert(path); | |
629 | assert(interface); | |
630 | assert(found); | |
631 | ||
632 | if (streq(path, "/org/freedesktop/machine1/machine/self")) { | |
633 | _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; | |
634 | _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; | |
635 | sd_bus_message *message; | |
636 | ||
637 | message = sd_bus_get_current_message(bus); | |
638 | if (!message) | |
639 | return 0; | |
640 | ||
641 | r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID|SD_BUS_CREDS_PIDFD, &creds); | |
642 | if (r < 0) | |
643 | return r; | |
644 | ||
645 | r = bus_creds_get_pidref(creds, &pidref); | |
646 | if (r < 0) | |
647 | return r; | |
648 | ||
649 | r = manager_get_machine_by_pidref(m, &pidref, &machine); | |
650 | if (r <= 0) | |
651 | return 0; | |
652 | } else { | |
653 | _cleanup_free_ char *e = NULL; | |
654 | const char *p; | |
655 | ||
656 | p = startswith(path, "/org/freedesktop/machine1/machine/"); | |
657 | if (!p) | |
658 | return 0; | |
659 | ||
660 | e = bus_label_unescape(p); | |
661 | if (!e) | |
662 | return -ENOMEM; | |
663 | ||
664 | machine = hashmap_get(m->machines, e); | |
665 | if (!machine) | |
666 | return 0; | |
667 | } | |
668 | ||
669 | *found = machine; | |
670 | return 1; | |
671 | } | |
672 | ||
673 | char* machine_bus_path(Machine *m) { | |
674 | _cleanup_free_ char *e = NULL; | |
675 | ||
676 | assert(m); | |
677 | ||
678 | e = bus_label_escape(m->name); | |
679 | if (!e) | |
680 | return NULL; | |
681 | ||
682 | return strjoin("/org/freedesktop/machine1/machine/", e); | |
683 | } | |
684 | ||
685 | static int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { | |
686 | _cleanup_strv_free_ char **l = NULL; | |
687 | Machine *machine = NULL; | |
688 | Manager *m = userdata; | |
689 | int r; | |
690 | ||
691 | assert(bus); | |
692 | assert(path); | |
693 | assert(nodes); | |
694 | ||
695 | HASHMAP_FOREACH(machine, m->machines) { | |
696 | char *p; | |
697 | ||
698 | p = machine_bus_path(machine); | |
699 | if (!p) | |
700 | return -ENOMEM; | |
701 | ||
702 | r = strv_consume(&l, p); | |
703 | if (r < 0) | |
704 | return r; | |
705 | } | |
706 | ||
707 | *nodes = TAKE_PTR(l); | |
708 | ||
709 | return 1; | |
710 | } | |
711 | ||
712 | static const sd_bus_vtable machine_vtable[] = { | |
713 | SD_BUS_VTABLE_START(0), | |
714 | SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST), | |
715 | SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128, offsetof(Machine, id), SD_BUS_VTABLE_PROPERTY_CONST), | |
716 | BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), | |
717 | SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST), | |
718 | SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST), | |
719 | SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), | |
720 | SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Machine, leader.pid), SD_BUS_VTABLE_PROPERTY_CONST), | |
721 | SD_BUS_PROPERTY("LeaderPIDFDId", "t", bus_property_get_pidfdid, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST), | |
722 | SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST), | |
723 | SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), | |
724 | SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST), | |
725 | SD_BUS_PROPERTY("VSockCID", "u", NULL, offsetof(Machine, vsock_cid), SD_BUS_VTABLE_PROPERTY_CONST), | |
726 | SD_BUS_PROPERTY("SSHAddress", "s", NULL, offsetof(Machine, ssh_address), SD_BUS_VTABLE_PROPERTY_CONST), | |
727 | SD_BUS_PROPERTY("SSHPrivateKeyPath", "s", NULL, offsetof(Machine, ssh_private_key_path), SD_BUS_VTABLE_PROPERTY_CONST), | |
728 | SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), | |
729 | ||
730 | SD_BUS_METHOD("Terminate", | |
731 | NULL, | |
732 | NULL, | |
733 | bus_machine_method_terminate, | |
734 | SD_BUS_VTABLE_UNPRIVILEGED), | |
735 | SD_BUS_METHOD_WITH_ARGS("Kill", | |
736 | SD_BUS_ARGS("s", whom, "i", signal), | |
737 | SD_BUS_NO_RESULT, | |
738 | bus_machine_method_kill, | |
739 | SD_BUS_VTABLE_UNPRIVILEGED), | |
740 | SD_BUS_METHOD_WITH_ARGS("GetAddresses", | |
741 | SD_BUS_NO_ARGS, | |
742 | SD_BUS_RESULT("a(iay)", addresses), | |
743 | bus_machine_method_get_addresses, | |
744 | SD_BUS_VTABLE_UNPRIVILEGED), | |
745 | SD_BUS_METHOD_WITH_ARGS("GetSSHInfo", | |
746 | SD_BUS_NO_ARGS, | |
747 | SD_BUS_RESULT("s", ssh_address, "s", ssh_private_key_path), | |
748 | bus_machine_method_get_ssh_info, | |
749 | SD_BUS_VTABLE_UNPRIVILEGED), | |
750 | SD_BUS_METHOD_WITH_ARGS("GetOSRelease", | |
751 | SD_BUS_NO_ARGS, | |
752 | SD_BUS_RESULT("a{ss}", fields), | |
753 | bus_machine_method_get_os_release, | |
754 | SD_BUS_VTABLE_UNPRIVILEGED), | |
755 | SD_BUS_METHOD_WITH_ARGS("GetUIDShift", | |
756 | SD_BUS_NO_ARGS, | |
757 | SD_BUS_RESULT("u", shift), | |
758 | bus_machine_method_get_uid_shift, | |
759 | SD_BUS_VTABLE_UNPRIVILEGED), | |
760 | SD_BUS_METHOD_WITH_ARGS("OpenPTY", | |
761 | SD_BUS_NO_ARGS, | |
762 | SD_BUS_RESULT("h", pty, "s", pty_path), | |
763 | bus_machine_method_open_pty, | |
764 | SD_BUS_VTABLE_UNPRIVILEGED), | |
765 | SD_BUS_METHOD_WITH_ARGS("OpenLogin", | |
766 | SD_BUS_NO_ARGS, | |
767 | SD_BUS_RESULT("h", pty, "s", pty_path), | |
768 | bus_machine_method_open_login, | |
769 | SD_BUS_VTABLE_UNPRIVILEGED), | |
770 | SD_BUS_METHOD_WITH_ARGS("OpenShell", | |
771 | SD_BUS_ARGS("s", user, "s", path, "as", args, "as", environment), | |
772 | SD_BUS_RESULT("h", pty, "s", pty_path), | |
773 | bus_machine_method_open_shell, | |
774 | SD_BUS_VTABLE_UNPRIVILEGED), | |
775 | SD_BUS_METHOD_WITH_ARGS("BindMount", | |
776 | SD_BUS_ARGS("s", source, "s", destination, "b", read_only, "b", mkdir), | |
777 | SD_BUS_NO_RESULT, | |
778 | bus_machine_method_bind_mount, | |
779 | SD_BUS_VTABLE_UNPRIVILEGED), | |
780 | SD_BUS_METHOD_WITH_ARGS("CopyFrom", | |
781 | SD_BUS_ARGS("s", source, "s", destination), | |
782 | SD_BUS_NO_RESULT, | |
783 | bus_machine_method_copy, | |
784 | SD_BUS_VTABLE_UNPRIVILEGED), | |
785 | SD_BUS_METHOD_WITH_ARGS("CopyTo", | |
786 | SD_BUS_ARGS("s", source, "s", destination), | |
787 | SD_BUS_NO_RESULT, | |
788 | bus_machine_method_copy, | |
789 | SD_BUS_VTABLE_UNPRIVILEGED), | |
790 | SD_BUS_METHOD_WITH_ARGS("CopyFromWithFlags", | |
791 | SD_BUS_ARGS("s", source, "s", destination, "t", flags), | |
792 | SD_BUS_NO_RESULT, | |
793 | bus_machine_method_copy, | |
794 | SD_BUS_VTABLE_UNPRIVILEGED), | |
795 | SD_BUS_METHOD_WITH_ARGS("CopyToWithFlags", | |
796 | SD_BUS_ARGS("s", source, "s", destination, "t", flags), | |
797 | SD_BUS_NO_RESULT, | |
798 | bus_machine_method_copy, | |
799 | SD_BUS_VTABLE_UNPRIVILEGED), | |
800 | SD_BUS_METHOD_WITH_ARGS("OpenRootDirectory", | |
801 | SD_BUS_NO_ARGS, | |
802 | SD_BUS_RESULT("h", fd), | |
803 | bus_machine_method_open_root_directory, | |
804 | SD_BUS_VTABLE_UNPRIVILEGED), | |
805 | ||
806 | SD_BUS_VTABLE_END | |
807 | }; | |
808 | ||
809 | const BusObjectImplementation machine_object = { | |
810 | "/org/freedesktop/machine1/machine", | |
811 | "org.freedesktop.machine1.Machine", | |
812 | .fallback_vtables = BUS_FALLBACK_VTABLES({machine_vtable, machine_object_find}), | |
813 | .node_enumerator = machine_node_enumerator, | |
814 | }; | |
815 | ||
816 | int machine_send_signal(Machine *m, bool new_machine) { | |
817 | _cleanup_free_ char *p = NULL; | |
818 | ||
819 | assert(m); | |
820 | ||
821 | p = machine_bus_path(m); | |
822 | if (!p) | |
823 | return -ENOMEM; | |
824 | ||
825 | return sd_bus_emit_signal( | |
826 | m->manager->bus, | |
827 | "/org/freedesktop/machine1", | |
828 | "org.freedesktop.machine1.Manager", | |
829 | new_machine ? "MachineNew" : "MachineRemoved", | |
830 | "so", m->name, p); | |
831 | } | |
832 | ||
833 | int machine_send_create_reply(Machine *m, sd_bus_error *error) { | |
834 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; | |
835 | _cleanup_free_ char *p = NULL; | |
836 | ||
837 | assert(m); | |
838 | ||
839 | if (!m->create_message) | |
840 | return 0; | |
841 | ||
842 | c = TAKE_PTR(m->create_message); | |
843 | ||
844 | if (error) | |
845 | return sd_bus_reply_method_error(c, error); | |
846 | ||
847 | /* Update the machine state file before we notify the client | |
848 | * about the result. */ | |
849 | machine_save(m); | |
850 | ||
851 | p = machine_bus_path(m); | |
852 | if (!p) | |
853 | return -ENOMEM; | |
854 | ||
855 | return sd_bus_reply_method_return(c, "o", p); | |
856 | } |