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