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