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