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