]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine-dbus.c
0a245247ec766672c15bf5c09f4e0a590d0ed443
[thirdparty/systemd.git] / src / machine / machine-dbus.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <sys/mount.h>
5 #include <sys/wait.h>
6
7 #include "alloc-util.h"
8 #include "bus-common-errors.h"
9 #include "bus-get-properties.h"
10 #include "bus-internal.h"
11 #include "bus-label.h"
12 #include "bus-locator.h"
13 #include "bus-polkit.h"
14 #include "copy.h"
15 #include "env-file.h"
16 #include "env-util.h"
17 #include "fd-util.h"
18 #include "fileio.h"
19 #include "format-util.h"
20 #include "fs-util.h"
21 #include "in-addr-util.h"
22 #include "io-util.h"
23 #include "local-addresses.h"
24 #include "machine-dbus.h"
25 #include "machine.h"
26 #include "missing_capability.h"
27 #include "mkdir.h"
28 #include "mount-util.h"
29 #include "mountpoint-util.h"
30 #include "namespace-util.h"
31 #include "os-util.h"
32 #include "path-util.h"
33 #include "process-util.h"
34 #include "signal-util.h"
35 #include "strv.h"
36 #include "terminal-util.h"
37 #include "tmpfile-util.h"
38 #include "user-util.h"
39
40 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
41 static BUS_DEFINE_PROPERTY_GET2(property_get_state, "s", Machine, machine_get_state, machine_state_to_string);
42
43 static int property_get_netif(
44 sd_bus *bus,
45 const char *path,
46 const char *interface,
47 const char *property,
48 sd_bus_message *reply,
49 void *userdata,
50 sd_bus_error *error) {
51
52 Machine *m = ASSERT_PTR(userdata);
53
54 assert(bus);
55 assert(reply);
56
57 assert_cc(sizeof(int) == sizeof(int32_t));
58
59 return sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int));
60 }
61
62 int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error) {
63 Machine *m = ASSERT_PTR(userdata);
64 int r;
65
66 assert(message);
67
68 const char *details[] = {
69 "machine", m->name,
70 "verb", "unregister",
71 NULL
72 };
73
74 r = bus_verify_polkit_async(
75 message,
76 CAP_KILL,
77 "org.freedesktop.machine1.manage-machines",
78 details,
79 false,
80 UID_INVALID,
81 &m->manager->polkit_registry,
82 error);
83 if (r < 0)
84 return r;
85 if (r == 0)
86 return 1; /* Will call us back */
87
88 r = machine_finalize(m);
89 if (r < 0)
90 return r;
91
92 return sd_bus_reply_method_return(message, NULL);
93 }
94
95 int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
96 Machine *m = ASSERT_PTR(userdata);
97 int r;
98
99 assert(message);
100
101 const char *details[] = {
102 "machine", m->name,
103 "verb", "terminate",
104 NULL
105 };
106
107 r = bus_verify_polkit_async(
108 message,
109 CAP_KILL,
110 "org.freedesktop.machine1.manage-machines",
111 details,
112 false,
113 UID_INVALID,
114 &m->manager->polkit_registry,
115 error);
116 if (r < 0)
117 return r;
118 if (r == 0)
119 return 1; /* Will call us back */
120
121 r = machine_stop(m);
122 if (r < 0)
123 return r;
124
125 return sd_bus_reply_method_return(message, NULL);
126 }
127
128 int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) {
129 Machine *m = ASSERT_PTR(userdata);
130 const char *swho;
131 int32_t signo;
132 KillWho who;
133 int r;
134
135 assert(message);
136
137 r = sd_bus_message_read(message, "si", &swho, &signo);
138 if (r < 0)
139 return r;
140
141 if (isempty(swho))
142 who = KILL_ALL;
143 else {
144 who = kill_who_from_string(swho);
145 if (who < 0)
146 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
147 }
148
149 if (!SIGNAL_VALID(signo))
150 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
151
152 const char *details[] = {
153 "machine", m->name,
154 "verb", "kill",
155 NULL
156 };
157
158 r = bus_verify_polkit_async(
159 message,
160 CAP_KILL,
161 "org.freedesktop.machine1.manage-machines",
162 details,
163 false,
164 UID_INVALID,
165 &m->manager->polkit_registry,
166 error);
167 if (r < 0)
168 return r;
169 if (r == 0)
170 return 1; /* Will call us back */
171
172 r = machine_kill(m, who, signo);
173 if (r < 0)
174 return r;
175
176 return sd_bus_reply_method_return(message, NULL);
177 }
178
179 int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) {
180 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
181 Machine *m = ASSERT_PTR(userdata);
182 int r;
183
184 assert(message);
185
186 r = sd_bus_message_new_method_return(message, &reply);
187 if (r < 0)
188 return r;
189
190 r = sd_bus_message_open_container(reply, 'a', "(iay)");
191 if (r < 0)
192 return r;
193
194 switch (m->class) {
195
196 case MACHINE_HOST: {
197 _cleanup_free_ struct local_address *addresses = NULL;
198 int n;
199
200 n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
201 if (n < 0)
202 return n;
203
204 for (int i = 0; i < n; i++) {
205 r = sd_bus_message_open_container(reply, 'r', "iay");
206 if (r < 0)
207 return r;
208
209 r = sd_bus_message_append(reply, "i", addresses[i].family);
210 if (r < 0)
211 return r;
212
213 r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family));
214 if (r < 0)
215 return r;
216
217 r = sd_bus_message_close_container(reply);
218 if (r < 0)
219 return r;
220 }
221
222 break;
223 }
224
225 case MACHINE_CONTAINER: {
226 _cleanup_close_pair_ int pair[2] = { -1, -1 };
227 _cleanup_free_ char *us = NULL, *them = NULL;
228 _cleanup_close_ int netns_fd = -1;
229 const char *p;
230 pid_t child;
231
232 r = readlink_malloc("/proc/self/ns/net", &us);
233 if (r < 0)
234 return r;
235
236 p = procfs_file_alloca(m->leader, "ns/net");
237 r = readlink_malloc(p, &them);
238 if (r < 0)
239 return r;
240
241 if (streq(us, them))
242 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
243
244 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL);
245 if (r < 0)
246 return r;
247
248 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
249 return -errno;
250
251 r = namespace_fork("(sd-addrns)", "(sd-addr)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
252 -1, -1, netns_fd, -1, -1, &child);
253 if (r < 0)
254 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
255 if (r == 0) {
256 _cleanup_free_ struct local_address *addresses = NULL;
257 struct local_address *a;
258 int i, n;
259
260 pair[0] = safe_close(pair[0]);
261
262 n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
263 if (n < 0)
264 _exit(EXIT_FAILURE);
265
266 for (a = addresses, i = 0; i < n; a++, i++) {
267 struct iovec iov[2] = {
268 { .iov_base = &a->family, .iov_len = sizeof(a->family) },
269 { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
270 };
271
272 r = writev(pair[1], iov, 2);
273 if (r < 0)
274 _exit(EXIT_FAILURE);
275 }
276
277 pair[1] = safe_close(pair[1]);
278
279 _exit(EXIT_SUCCESS);
280 }
281
282 pair[1] = safe_close(pair[1]);
283
284 for (;;) {
285 int family;
286 ssize_t n;
287 union in_addr_union in_addr;
288 struct iovec iov[2];
289 struct msghdr mh = {
290 .msg_iov = iov,
291 .msg_iovlen = 2,
292 };
293
294 iov[0] = IOVEC_MAKE(&family, sizeof(family));
295 iov[1] = IOVEC_MAKE(&in_addr, sizeof(in_addr));
296
297 n = recvmsg(pair[0], &mh, 0);
298 if (n < 0)
299 return -errno;
300 if ((size_t) n < sizeof(family))
301 break;
302
303 r = sd_bus_message_open_container(reply, 'r', "iay");
304 if (r < 0)
305 return r;
306
307 r = sd_bus_message_append(reply, "i", family);
308 if (r < 0)
309 return r;
310
311 switch (family) {
312
313 case AF_INET:
314 if (n != sizeof(struct in_addr) + sizeof(family))
315 return -EIO;
316
317 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
318 break;
319
320 case AF_INET6:
321 if (n != sizeof(struct in6_addr) + sizeof(family))
322 return -EIO;
323
324 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
325 break;
326 }
327 if (r < 0)
328 return r;
329
330 r = sd_bus_message_close_container(reply);
331 if (r < 0)
332 return r;
333 }
334
335 r = wait_for_terminate_and_check("(sd-addrns)", child, 0);
336 if (r < 0)
337 return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
338 if (r != EXIT_SUCCESS)
339 return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
340 break;
341 }
342
343 default:
344 return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
345 }
346
347 r = sd_bus_message_close_container(reply);
348 if (r < 0)
349 return r;
350
351 return sd_bus_send(NULL, reply, NULL);
352 }
353
354 #define EXIT_NOT_FOUND 2
355
356 int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
357 _cleanup_strv_free_ char **l = NULL;
358 Machine *m = ASSERT_PTR(userdata);
359 int r;
360
361 assert(message);
362
363 switch (m->class) {
364
365 case MACHINE_HOST:
366 r = load_os_release_pairs(NULL, &l);
367 if (r < 0)
368 return r;
369
370 break;
371
372 case MACHINE_CONTAINER: {
373 _cleanup_close_ int mntns_fd = -1, root_fd = -1, pidns_fd = -1;
374 _cleanup_close_pair_ int pair[2] = { -1, -1 };
375 _cleanup_fclose_ FILE *f = NULL;
376 pid_t child;
377
378 r = namespace_open(m->leader, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
379 if (r < 0)
380 return r;
381
382 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
383 return -errno;
384
385 r = namespace_fork("(sd-osrelns)", "(sd-osrel)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
386 pidns_fd, mntns_fd, -1, -1, root_fd,
387 &child);
388 if (r < 0)
389 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
390 if (r == 0) {
391 int fd = -1;
392
393 pair[0] = safe_close(pair[0]);
394
395 r = open_os_release(NULL, NULL, &fd);
396 if (r == -ENOENT)
397 _exit(EXIT_NOT_FOUND);
398 if (r < 0)
399 _exit(EXIT_FAILURE);
400
401 r = copy_bytes(fd, pair[1], UINT64_MAX, 0);
402 if (r < 0)
403 _exit(EXIT_FAILURE);
404
405 _exit(EXIT_SUCCESS);
406 }
407
408 pair[1] = safe_close(pair[1]);
409
410 f = take_fdopen(&pair[0], "r");
411 if (!f)
412 return -errno;
413
414 r = load_env_file_pairs(f, "/etc/os-release", &l);
415 if (r < 0)
416 return r;
417
418 r = wait_for_terminate_and_check("(sd-osrelns)", child, 0);
419 if (r < 0)
420 return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
421 if (r == EXIT_NOT_FOUND)
422 return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information");
423 if (r != EXIT_SUCCESS)
424 return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
425
426 break;
427 }
428
429 default:
430 return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
431 }
432
433 return bus_reply_pair_array(message, l);
434 }
435
436 int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) {
437 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
438 _cleanup_free_ char *pty_name = NULL;
439 _cleanup_close_ int master = -1;
440 Machine *m = ASSERT_PTR(userdata);
441 int r;
442
443 assert(message);
444
445 const char *details[] = {
446 "machine", m->name,
447 NULL
448 };
449
450 r = bus_verify_polkit_async(
451 message,
452 CAP_SYS_ADMIN,
453 m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
454 details,
455 false,
456 UID_INVALID,
457 &m->manager->polkit_registry,
458 error);
459 if (r < 0)
460 return r;
461 if (r == 0)
462 return 1; /* Will call us back */
463
464 master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
465 if (master < 0)
466 return master;
467
468 r = sd_bus_message_new_method_return(message, &reply);
469 if (r < 0)
470 return r;
471
472 r = sd_bus_message_append(reply, "hs", master, pty_name);
473 if (r < 0)
474 return r;
475
476 return sd_bus_send(NULL, reply, NULL);
477 }
478
479 static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
480 int r;
481
482 assert(m);
483 assert(ret);
484
485 switch (m->class) {
486
487 case MACHINE_HOST:
488 *ret = NULL;
489 break;
490
491 case MACHINE_CONTAINER: {
492 _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
493 char *address;
494
495 r = sd_bus_new(&bus);
496 if (r < 0)
497 return r;
498
499 if (asprintf(&address, "x-machine-unix:pid=%" PID_PRI, m->leader) < 0)
500 return -ENOMEM;
501
502 bus->address = address;
503 bus->bus_client = true;
504 bus->trusted = false;
505 bus->is_system = true;
506
507 r = sd_bus_start(bus);
508 if (r == -ENOENT)
509 return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name);
510 if (r < 0)
511 return r;
512
513 *ret = TAKE_PTR(bus);
514 break;
515 }
516
517 default:
518 return -EOPNOTSUPP;
519 }
520
521 return 0;
522 }
523
524 int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) {
525 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
526 _cleanup_free_ char *pty_name = NULL;
527 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
528 _cleanup_close_ int master = -1;
529 sd_bus *container_bus = NULL;
530 Machine *m = ASSERT_PTR(userdata);
531 const char *p, *getty;
532 int r;
533
534 assert(message);
535
536 const char *details[] = {
537 "machine", m->name,
538 "verb", "login",
539 NULL
540 };
541
542 r = bus_verify_polkit_async(
543 message,
544 CAP_SYS_ADMIN,
545 m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
546 details,
547 false,
548 UID_INVALID,
549 &m->manager->polkit_registry,
550 error);
551 if (r < 0)
552 return r;
553 if (r == 0)
554 return 1; /* Will call us back */
555
556 master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
557 if (master < 0)
558 return master;
559
560 p = path_startswith(pty_name, "/dev/pts/");
561 assert(p);
562
563 r = container_bus_new(m, error, &allocated_bus);
564 if (r < 0)
565 return r;
566
567 container_bus = allocated_bus ?: m->manager->bus;
568
569 getty = strjoina("container-getty@", p, ".service");
570
571 r = bus_call_method(container_bus, bus_systemd_mgr, "StartUnit", error, NULL, "ss", getty, "replace");
572 if (r < 0)
573 return r;
574
575 r = sd_bus_message_new_method_return(message, &reply);
576 if (r < 0)
577 return r;
578
579 r = sd_bus_message_append(reply, "hs", master, pty_name);
580 if (r < 0)
581 return r;
582
583 return sd_bus_send(NULL, reply, NULL);
584 }
585
586 int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) {
587 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *tm = NULL;
588 _cleanup_free_ char *pty_name = NULL;
589 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
590 sd_bus *container_bus = NULL;
591 _cleanup_close_ int master = -1, slave = -1;
592 _cleanup_strv_free_ char **env = NULL, **args_wire = NULL, **args = NULL;
593 Machine *m = ASSERT_PTR(userdata);
594 const char *p, *unit, *user, *path, *description, *utmp_id;
595 int r;
596
597 assert(message);
598
599 r = sd_bus_message_read(message, "ss", &user, &path);
600 if (r < 0)
601 return r;
602 user = isempty(user) ? "root" : user;
603 r = sd_bus_message_read_strv(message, &args_wire);
604 if (r < 0)
605 return r;
606 if (isempty(path)) {
607 path = "/bin/sh";
608
609 args = new0(char*, 3 + 1);
610 if (!args)
611 return -ENOMEM;
612 args[0] = strdup("sh");
613 if (!args[0])
614 return -ENOMEM;
615 args[1] = strdup("-c");
616 if (!args[1])
617 return -ENOMEM;
618 r = asprintf(&args[2],
619 "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
620 "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
621 user);
622 if (r < 0) {
623 args[2] = NULL;
624 return -ENOMEM;
625 }
626 } else {
627 if (!path_is_absolute(path))
628 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path);
629 args = TAKE_PTR(args_wire);
630 if (strv_isempty(args)) {
631 args = strv_free(args);
632
633 args = strv_new(path);
634 if (!args)
635 return -ENOMEM;
636 }
637 }
638
639 r = sd_bus_message_read_strv(message, &env);
640 if (r < 0)
641 return r;
642 if (!strv_env_is_valid(env))
643 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
644
645 const char *details[] = {
646 "machine", m->name,
647 "user", user,
648 "program", path,
649 NULL
650 };
651
652 r = bus_verify_polkit_async(
653 message,
654 CAP_SYS_ADMIN,
655 m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
656 details,
657 false,
658 UID_INVALID,
659 &m->manager->polkit_registry,
660 error);
661 if (r < 0)
662 return r;
663 if (r == 0)
664 return 1; /* Will call us back */
665
666 master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
667 if (master < 0)
668 return master;
669
670 p = path_startswith(pty_name, "/dev/pts/");
671 assert(p);
672
673 slave = machine_open_terminal(m, pty_name, O_RDWR|O_NOCTTY|O_CLOEXEC);
674 if (slave < 0)
675 return slave;
676
677 utmp_id = path_startswith(pty_name, "/dev/");
678 assert(utmp_id);
679
680 r = container_bus_new(m, error, &allocated_bus);
681 if (r < 0)
682 return r;
683
684 container_bus = allocated_bus ?: m->manager->bus;
685
686 r = bus_message_new_method_call(container_bus, &tm, bus_systemd_mgr, "StartTransientUnit");
687 if (r < 0)
688 return r;
689
690 /* Name and mode */
691 unit = strjoina("container-shell@", p, ".service");
692 r = sd_bus_message_append(tm, "ss", unit, "fail");
693 if (r < 0)
694 return r;
695
696 /* Properties */
697 r = sd_bus_message_open_container(tm, 'a', "(sv)");
698 if (r < 0)
699 return r;
700
701 description = strjoina("Shell for User ", user);
702 r = sd_bus_message_append(tm,
703 "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
704 "Description", "s", description,
705 "StandardInputFileDescriptor", "h", slave,
706 "StandardOutputFileDescriptor", "h", slave,
707 "StandardErrorFileDescriptor", "h", slave,
708 "SendSIGHUP", "b", true,
709 "IgnoreSIGPIPE", "b", false,
710 "KillMode", "s", "mixed",
711 "TTYPath", "s", pty_name,
712 "TTYReset", "b", true,
713 "UtmpIdentifier", "s", utmp_id,
714 "UtmpMode", "s", "user",
715 "PAMName", "s", "login",
716 "WorkingDirectory", "s", "-~");
717 if (r < 0)
718 return r;
719
720 r = sd_bus_message_append(tm, "(sv)", "User", "s", user);
721 if (r < 0)
722 return r;
723
724 if (!strv_isempty(env)) {
725 r = sd_bus_message_open_container(tm, 'r', "sv");
726 if (r < 0)
727 return r;
728
729 r = sd_bus_message_append(tm, "s", "Environment");
730 if (r < 0)
731 return r;
732
733 r = sd_bus_message_open_container(tm, 'v', "as");
734 if (r < 0)
735 return r;
736
737 r = sd_bus_message_append_strv(tm, env);
738 if (r < 0)
739 return r;
740
741 r = sd_bus_message_close_container(tm);
742 if (r < 0)
743 return r;
744
745 r = sd_bus_message_close_container(tm);
746 if (r < 0)
747 return r;
748 }
749
750 /* Exec container */
751 r = sd_bus_message_open_container(tm, 'r', "sv");
752 if (r < 0)
753 return r;
754
755 r = sd_bus_message_append(tm, "s", "ExecStart");
756 if (r < 0)
757 return r;
758
759 r = sd_bus_message_open_container(tm, 'v', "a(sasb)");
760 if (r < 0)
761 return r;
762
763 r = sd_bus_message_open_container(tm, 'a', "(sasb)");
764 if (r < 0)
765 return r;
766
767 r = sd_bus_message_open_container(tm, 'r', "sasb");
768 if (r < 0)
769 return r;
770
771 r = sd_bus_message_append(tm, "s", path);
772 if (r < 0)
773 return r;
774
775 r = sd_bus_message_append_strv(tm, args);
776 if (r < 0)
777 return r;
778
779 r = sd_bus_message_append(tm, "b", true);
780 if (r < 0)
781 return r;
782
783 r = sd_bus_message_close_container(tm);
784 if (r < 0)
785 return r;
786
787 r = sd_bus_message_close_container(tm);
788 if (r < 0)
789 return r;
790
791 r = sd_bus_message_close_container(tm);
792 if (r < 0)
793 return r;
794
795 r = sd_bus_message_close_container(tm);
796 if (r < 0)
797 return r;
798
799 r = sd_bus_message_close_container(tm);
800 if (r < 0)
801 return r;
802
803 /* Auxiliary units */
804 r = sd_bus_message_append(tm, "a(sa(sv))", 0);
805 if (r < 0)
806 return r;
807
808 r = sd_bus_call(container_bus, tm, 0, error, NULL);
809 if (r < 0)
810 return r;
811
812 slave = safe_close(slave);
813
814 r = sd_bus_message_new_method_return(message, &reply);
815 if (r < 0)
816 return r;
817
818 r = sd_bus_message_append(reply, "hs", master, pty_name);
819 if (r < 0)
820 return r;
821
822 return sd_bus_send(NULL, reply, NULL);
823 }
824
825 int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) {
826 int read_only, make_file_or_directory;
827 const char *dest, *src, *propagate_directory;
828 Machine *m = ASSERT_PTR(userdata);
829 uid_t uid;
830 int r;
831
832 assert(message);
833
834 if (m->class != MACHINE_CONTAINER)
835 return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines.");
836
837 r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_file_or_directory);
838 if (r < 0)
839 return r;
840
841 if (!path_is_absolute(src) || !path_is_normalized(src))
842 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized.");
843
844 if (isempty(dest))
845 dest = src;
846 else if (!path_is_absolute(dest) || !path_is_normalized(dest))
847 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
848
849 const char *details[] = {
850 "machine", m->name,
851 "verb", "bind",
852 "src", src,
853 "dest", dest,
854 NULL
855 };
856
857 r = bus_verify_polkit_async(
858 message,
859 CAP_SYS_ADMIN,
860 "org.freedesktop.machine1.manage-machines",
861 details,
862 false,
863 UID_INVALID,
864 &m->manager->polkit_registry,
865 error);
866 if (r < 0)
867 return r;
868 if (r == 0)
869 return 1; /* Will call us back */
870
871 r = machine_get_uid_shift(m, &uid);
872 if (r < 0)
873 return r;
874 if (uid != 0)
875 return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied.");
876
877 propagate_directory = strjoina("/run/systemd/nspawn/propagate/", m->name);
878 r = bind_mount_in_namespace(m->leader,
879 propagate_directory,
880 "/run/host/incoming/",
881 src, dest, read_only, make_file_or_directory);
882 if (r < 0)
883 return sd_bus_error_set_errnof(error, r, "Failed to mount %s on %s in machine's namespace: %m", src, dest);
884
885 return sd_bus_reply_method_return(message, NULL);
886 }
887
888 int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
889 _cleanup_free_ char *host_basename = NULL, *container_basename = NULL;
890 const char *src, *dest, *host_path, *container_path;
891 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
892 CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS;
893 _cleanup_close_ int hostfd = -1;
894 Machine *m = ASSERT_PTR(userdata);
895 bool copy_from;
896 pid_t child;
897 uid_t uid_shift;
898 int r;
899
900 assert(message);
901
902 if (m->manager->n_operations >= OPERATIONS_MAX)
903 return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies.");
904
905 if (m->class != MACHINE_CONTAINER)
906 return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines.");
907
908 r = sd_bus_message_read(message, "ss", &src, &dest);
909 if (r < 0)
910 return r;
911
912 if (endswith(sd_bus_message_get_member(message), "WithFlags")) {
913 uint64_t raw_flags;
914
915 r = sd_bus_message_read(message, "t", &raw_flags);
916 if (r < 0)
917 return r;
918
919 if ((raw_flags & ~_MACHINE_COPY_FLAGS_MASK_PUBLIC) != 0)
920 return -EINVAL;
921
922 if (raw_flags & MACHINE_COPY_REPLACE)
923 copy_flags |= COPY_REPLACE;
924 }
925
926 if (!path_is_absolute(src))
927 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute.");
928
929 if (isempty(dest))
930 dest = src;
931 else if (!path_is_absolute(dest))
932 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute.");
933
934 const char *details[] = {
935 "machine", m->name,
936 "verb", "copy",
937 "src", src,
938 "dest", dest,
939 NULL
940 };
941
942 r = bus_verify_polkit_async(
943 message,
944 CAP_SYS_ADMIN,
945 "org.freedesktop.machine1.manage-machines",
946 details,
947 false,
948 UID_INVALID,
949 &m->manager->polkit_registry,
950 error);
951 if (r < 0)
952 return r;
953 if (r == 0)
954 return 1; /* Will call us back */
955
956 r = machine_get_uid_shift(m, &uid_shift);
957 if (r < 0)
958 return r;
959
960 copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom");
961
962 if (copy_from) {
963 container_path = src;
964 host_path = dest;
965 } else {
966 host_path = src;
967 container_path = dest;
968 }
969
970 r = path_extract_filename(host_path, &host_basename);
971 if (r < 0)
972 return sd_bus_error_set_errnof(error, r, "Failed to extract file name of '%s' path: %m", host_path);
973
974 r = path_extract_filename(container_path, &container_basename);
975 if (r < 0)
976 return sd_bus_error_set_errnof(error, r, "Failed to extract file name of '%s' path: %m", container_path);
977
978 hostfd = open_parent(host_path, O_CLOEXEC, 0);
979 if (hostfd < 0)
980 return sd_bus_error_set_errnof(error, hostfd, "Failed to open host directory %s: %m", host_path);
981
982 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
983 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
984
985 r = safe_fork("(sd-copy)", FORK_RESET_SIGNALS, &child);
986 if (r < 0)
987 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
988 if (r == 0) {
989 int containerfd;
990 const char *q;
991 int mntfd;
992
993 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
994
995 q = procfs_file_alloca(m->leader, "ns/mnt");
996 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
997 if (mntfd < 0) {
998 r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
999 goto child_fail;
1000 }
1001
1002 if (setns(mntfd, CLONE_NEWNS) < 0) {
1003 r = log_error_errno(errno, "Failed to join namespace of leader: %m");
1004 goto child_fail;
1005 }
1006
1007 containerfd = open_parent(container_path, O_CLOEXEC, 0);
1008 if (containerfd < 0) {
1009 r = log_error_errno(containerfd, "Failed to open destination directory: %m");
1010 goto child_fail;
1011 }
1012
1013 /* Run the actual copy operation. Note that when an UID shift is set we'll either clamp the UID/GID to
1014 * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
1015 * the UID/GIDs as they are. */
1016 if (copy_from)
1017 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags);
1018 else
1019 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags);
1020
1021 hostfd = safe_close(hostfd);
1022 containerfd = safe_close(containerfd);
1023
1024 if (r < 0) {
1025 r = log_error_errno(r, "Failed to copy tree: %m");
1026 goto child_fail;
1027 }
1028
1029 _exit(EXIT_SUCCESS);
1030
1031 child_fail:
1032 (void) write(errno_pipe_fd[1], &r, sizeof(r));
1033 _exit(EXIT_FAILURE);
1034 }
1035
1036 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
1037
1038 /* Copying might take a while, hence install a watch on the child, and return */
1039
1040 r = operation_new(m->manager, m, child, message, errno_pipe_fd[0], NULL);
1041 if (r < 0) {
1042 (void) sigkill_wait(child);
1043 return r;
1044 }
1045 errno_pipe_fd[0] = -1;
1046
1047 return 1;
1048 }
1049
1050 int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) {
1051 _cleanup_close_ int fd = -1;
1052 Machine *m = ASSERT_PTR(userdata);
1053 int r;
1054
1055 assert(message);
1056
1057 const char *details[] = {
1058 "machine", m->name,
1059 "verb", "open_root_directory",
1060 NULL
1061 };
1062
1063 r = bus_verify_polkit_async(
1064 message,
1065 CAP_SYS_ADMIN,
1066 "org.freedesktop.machine1.manage-machines",
1067 details,
1068 false,
1069 UID_INVALID,
1070 &m->manager->polkit_registry,
1071 error);
1072 if (r < 0)
1073 return r;
1074 if (r == 0)
1075 return 1; /* Will call us back */
1076
1077 switch (m->class) {
1078
1079 case MACHINE_HOST:
1080 fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
1081 if (fd < 0)
1082 return -errno;
1083
1084 break;
1085
1086 case MACHINE_CONTAINER: {
1087 _cleanup_close_ int mntns_fd = -1, root_fd = -1;
1088 _cleanup_close_pair_ int pair[2] = { -1, -1 };
1089 pid_t child;
1090
1091 r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
1092 if (r < 0)
1093 return r;
1094
1095 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1096 return -errno;
1097
1098 r = namespace_fork("(sd-openrootns)", "(sd-openroot)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
1099 -1, mntns_fd, -1, -1, root_fd, &child);
1100 if (r < 0)
1101 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
1102 if (r == 0) {
1103 _cleanup_close_ int dfd = -1;
1104
1105 pair[0] = safe_close(pair[0]);
1106
1107 dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
1108 if (dfd < 0)
1109 _exit(EXIT_FAILURE);
1110
1111 r = send_one_fd(pair[1], dfd, 0);
1112 dfd = safe_close(dfd);
1113 if (r < 0)
1114 _exit(EXIT_FAILURE);
1115
1116 _exit(EXIT_SUCCESS);
1117 }
1118
1119 pair[1] = safe_close(pair[1]);
1120
1121 r = wait_for_terminate_and_check("(sd-openrootns)", child, 0);
1122 if (r < 0)
1123 return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
1124 if (r != EXIT_SUCCESS)
1125 return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
1126
1127 fd = receive_one_fd(pair[0], MSG_DONTWAIT);
1128 if (fd < 0)
1129 return fd;
1130
1131 break;
1132 }
1133
1134 default:
1135 return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines.");
1136 }
1137
1138 return sd_bus_reply_method_return(message, "h", fd);
1139 }
1140
1141 int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) {
1142 Machine *m = ASSERT_PTR(userdata);
1143 uid_t shift = 0;
1144 int r;
1145
1146 assert(message);
1147
1148 /* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but
1149 * we kinda have to for this. */
1150
1151 if (m->class == MACHINE_HOST)
1152 return sd_bus_reply_method_return(message, "u", UINT32_C(0));
1153
1154 if (m->class != MACHINE_CONTAINER)
1155 return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines.");
1156
1157 r = machine_get_uid_shift(m, &shift);
1158 if (r == -ENXIO)
1159 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m->name);
1160 if (r < 0)
1161 return r;
1162
1163 return sd_bus_reply_method_return(message, "u", (uint32_t) shift);
1164 }
1165
1166 static int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1167 Manager *m = ASSERT_PTR(userdata);
1168 Machine *machine;
1169 int r;
1170
1171 assert(bus);
1172 assert(path);
1173 assert(interface);
1174 assert(found);
1175
1176 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
1177 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
1178 sd_bus_message *message;
1179 pid_t pid;
1180
1181 message = sd_bus_get_current_message(bus);
1182 if (!message)
1183 return 0;
1184
1185 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
1186 if (r < 0)
1187 return r;
1188
1189 r = sd_bus_creds_get_pid(creds, &pid);
1190 if (r < 0)
1191 return r;
1192
1193 r = manager_get_machine_by_pid(m, pid, &machine);
1194 if (r <= 0)
1195 return 0;
1196 } else {
1197 _cleanup_free_ char *e = NULL;
1198 const char *p;
1199
1200 p = startswith(path, "/org/freedesktop/machine1/machine/");
1201 if (!p)
1202 return 0;
1203
1204 e = bus_label_unescape(p);
1205 if (!e)
1206 return -ENOMEM;
1207
1208 machine = hashmap_get(m->machines, e);
1209 if (!machine)
1210 return 0;
1211 }
1212
1213 *found = machine;
1214 return 1;
1215 }
1216
1217 char *machine_bus_path(Machine *m) {
1218 _cleanup_free_ char *e = NULL;
1219
1220 assert(m);
1221
1222 e = bus_label_escape(m->name);
1223 if (!e)
1224 return NULL;
1225
1226 return strjoin("/org/freedesktop/machine1/machine/", e);
1227 }
1228
1229 static int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1230 _cleanup_strv_free_ char **l = NULL;
1231 Machine *machine = NULL;
1232 Manager *m = userdata;
1233 int r;
1234
1235 assert(bus);
1236 assert(path);
1237 assert(nodes);
1238
1239 HASHMAP_FOREACH(machine, m->machines) {
1240 char *p;
1241
1242 p = machine_bus_path(machine);
1243 if (!p)
1244 return -ENOMEM;
1245
1246 r = strv_consume(&l, p);
1247 if (r < 0)
1248 return r;
1249 }
1250
1251 *nodes = TAKE_PTR(l);
1252
1253 return 1;
1254 }
1255
1256 static const sd_bus_vtable machine_vtable[] = {
1257 SD_BUS_VTABLE_START(0),
1258 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
1259 SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128, offsetof(Machine, id), SD_BUS_VTABLE_PROPERTY_CONST),
1260 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
1261 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
1262 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
1263 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
1264 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
1265 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
1266 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
1267 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
1268 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
1269
1270 SD_BUS_METHOD("Terminate",
1271 NULL,
1272 NULL,
1273 bus_machine_method_terminate,
1274 SD_BUS_VTABLE_UNPRIVILEGED),
1275 SD_BUS_METHOD_WITH_ARGS("Kill",
1276 SD_BUS_ARGS("s", who, "i", signal),
1277 SD_BUS_NO_RESULT,
1278 bus_machine_method_kill,
1279 SD_BUS_VTABLE_UNPRIVILEGED),
1280 SD_BUS_METHOD_WITH_ARGS("GetAddresses",
1281 SD_BUS_NO_ARGS,
1282 SD_BUS_RESULT("a(iay)", addresses),
1283 bus_machine_method_get_addresses,
1284 SD_BUS_VTABLE_UNPRIVILEGED),
1285 SD_BUS_METHOD_WITH_ARGS("GetOSRelease",
1286 SD_BUS_NO_ARGS,
1287 SD_BUS_RESULT("a{ss}", fields),
1288 bus_machine_method_get_os_release,
1289 SD_BUS_VTABLE_UNPRIVILEGED),
1290 SD_BUS_METHOD_WITH_ARGS("GetUIDShift",
1291 SD_BUS_NO_ARGS,
1292 SD_BUS_RESULT("u", shift),
1293 bus_machine_method_get_uid_shift,
1294 SD_BUS_VTABLE_UNPRIVILEGED),
1295 SD_BUS_METHOD_WITH_ARGS("OpenPTY",
1296 SD_BUS_NO_ARGS,
1297 SD_BUS_RESULT("h", pty, "s", pty_path),
1298 bus_machine_method_open_pty,
1299 SD_BUS_VTABLE_UNPRIVILEGED),
1300 SD_BUS_METHOD_WITH_ARGS("OpenLogin",
1301 SD_BUS_NO_ARGS,
1302 SD_BUS_RESULT("h", pty, "s", pty_path),
1303 bus_machine_method_open_login,
1304 SD_BUS_VTABLE_UNPRIVILEGED),
1305 SD_BUS_METHOD_WITH_ARGS("OpenShell",
1306 SD_BUS_ARGS("s", user, "s", path, "as", args, "as", environment),
1307 SD_BUS_RESULT("h", pty, "s", pty_path),
1308 bus_machine_method_open_shell,
1309 SD_BUS_VTABLE_UNPRIVILEGED),
1310 SD_BUS_METHOD_WITH_ARGS("BindMount",
1311 SD_BUS_ARGS("s", source, "s", destination, "b", read_only, "b", mkdir),
1312 SD_BUS_NO_RESULT,
1313 bus_machine_method_bind_mount,
1314 SD_BUS_VTABLE_UNPRIVILEGED),
1315 SD_BUS_METHOD_WITH_ARGS("CopyFrom",
1316 SD_BUS_ARGS("s", source, "s", destination),
1317 SD_BUS_NO_RESULT,
1318 bus_machine_method_copy,
1319 SD_BUS_VTABLE_UNPRIVILEGED),
1320 SD_BUS_METHOD_WITH_ARGS("CopyTo",
1321 SD_BUS_ARGS("s", source, "s", destination),
1322 SD_BUS_NO_RESULT,
1323 bus_machine_method_copy,
1324 SD_BUS_VTABLE_UNPRIVILEGED),
1325 SD_BUS_METHOD_WITH_ARGS("CopyFromWithFlags",
1326 SD_BUS_ARGS("s", source, "s", destination, "t", flags),
1327 SD_BUS_NO_RESULT,
1328 bus_machine_method_copy,
1329 SD_BUS_VTABLE_UNPRIVILEGED),
1330 SD_BUS_METHOD_WITH_ARGS("CopyToWithFlags",
1331 SD_BUS_ARGS("s", source, "s", destination, "t", flags),
1332 SD_BUS_NO_RESULT,
1333 bus_machine_method_copy,
1334 SD_BUS_VTABLE_UNPRIVILEGED),
1335 SD_BUS_METHOD_WITH_ARGS("OpenRootDirectory",
1336 SD_BUS_NO_ARGS,
1337 SD_BUS_RESULT("h", fd),
1338 bus_machine_method_open_root_directory,
1339 SD_BUS_VTABLE_UNPRIVILEGED),
1340
1341 SD_BUS_VTABLE_END
1342 };
1343
1344 const BusObjectImplementation machine_object = {
1345 "/org/freedesktop/machine1/machine",
1346 "org.freedesktop.machine1.Machine",
1347 .fallback_vtables = BUS_FALLBACK_VTABLES({machine_vtable, machine_object_find}),
1348 .node_enumerator = machine_node_enumerator,
1349 };
1350
1351 int machine_send_signal(Machine *m, bool new_machine) {
1352 _cleanup_free_ char *p = NULL;
1353
1354 assert(m);
1355
1356 p = machine_bus_path(m);
1357 if (!p)
1358 return -ENOMEM;
1359
1360 return sd_bus_emit_signal(
1361 m->manager->bus,
1362 "/org/freedesktop/machine1",
1363 "org.freedesktop.machine1.Manager",
1364 new_machine ? "MachineNew" : "MachineRemoved",
1365 "so", m->name, p);
1366 }
1367
1368 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
1369 _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
1370 _cleanup_free_ char *p = NULL;
1371
1372 assert(m);
1373
1374 if (!m->create_message)
1375 return 0;
1376
1377 c = TAKE_PTR(m->create_message);
1378
1379 if (error)
1380 return sd_bus_reply_method_error(c, error);
1381
1382 /* Update the machine state file before we notify the client
1383 * about the result. */
1384 machine_save(m);
1385
1386 p = machine_bus_path(m);
1387 if (!p)
1388 return -ENOMEM;
1389
1390 return sd_bus_reply_method_return(c, "o", p);
1391 }