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