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