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