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