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