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