]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine-dbus.c
machined: refuse certain operation on non-container machines, since they cannot work...
[thirdparty/systemd.git] / src / machine / machine-dbus.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24 #include <arpa/inet.h>
25
26 #include "bus-util.h"
27 #include "bus-label.h"
28 #include "strv.h"
29 #include "bus-common-errors.h"
30 #include "copy.h"
31 #include "fileio.h"
32 #include "in-addr-util.h"
33 #include "local-addresses.h"
34 #include "path-util.h"
35 #include "bus-internal.h"
36 #include "machine.h"
37 #include "machine-dbus.h"
38
39 static int property_get_id(
40 sd_bus *bus,
41 const char *path,
42 const char *interface,
43 const char *property,
44 sd_bus_message *reply,
45 void *userdata,
46 sd_bus_error *error) {
47
48 Machine *m = userdata;
49 int r;
50
51 assert(bus);
52 assert(reply);
53 assert(m);
54
55 r = sd_bus_message_append_array(reply, 'y', &m->id, 16);
56 if (r < 0)
57 return r;
58
59 return 1;
60 }
61
62 static int property_get_state(
63 sd_bus *bus,
64 const char *path,
65 const char *interface,
66 const char *property,
67 sd_bus_message *reply,
68 void *userdata,
69 sd_bus_error *error) {
70
71 Machine *m = userdata;
72 const char *state;
73 int r;
74
75 assert(bus);
76 assert(reply);
77 assert(m);
78
79 state = machine_state_to_string(machine_get_state(m));
80
81 r = sd_bus_message_append_basic(reply, 's', state);
82 if (r < 0)
83 return r;
84
85 return 1;
86 }
87
88 static int property_get_netif(
89 sd_bus *bus,
90 const char *path,
91 const char *interface,
92 const char *property,
93 sd_bus_message *reply,
94 void *userdata,
95 sd_bus_error *error) {
96
97 Machine *m = userdata;
98 int r;
99
100 assert(bus);
101 assert(reply);
102 assert(m);
103
104 assert_cc(sizeof(int) == sizeof(int32_t));
105
106 r = sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int));
107 if (r < 0)
108 return r;
109
110 return 1;
111 }
112
113 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
114
115 int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
116 Machine *m = userdata;
117 int r;
118
119 assert(bus);
120 assert(message);
121 assert(m);
122
123 r = machine_stop(m);
124 if (r < 0)
125 return r;
126
127 return sd_bus_reply_method_return(message, NULL);
128 }
129
130 int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
131 Machine *m = userdata;
132 const char *swho;
133 int32_t signo;
134 KillWho who;
135 int r;
136
137 assert(bus);
138 assert(message);
139 assert(m);
140
141 r = sd_bus_message_read(message, "si", &swho, &signo);
142 if (r < 0)
143 return r;
144
145 if (isempty(swho))
146 who = KILL_ALL;
147 else {
148 who = kill_who_from_string(swho);
149 if (who < 0)
150 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
151 }
152
153 if (signo <= 0 || signo >= _NSIG)
154 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
155
156 r = machine_kill(m, who, signo);
157 if (r < 0)
158 return r;
159
160 return sd_bus_reply_method_return(message, NULL);
161 }
162
163 int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
164 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
165 _cleanup_close_pair_ int pair[2] = { -1, -1 };
166 _cleanup_free_ char *us = NULL, *them = NULL;
167 _cleanup_close_ int netns_fd = -1;
168 Machine *m = userdata;
169 const char *p;
170 siginfo_t si;
171 pid_t child;
172 int r;
173
174 assert(bus);
175 assert(message);
176 assert(m);
177
178 if (m->class != MACHINE_CONTAINER)
179 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
180
181 r = readlink_malloc("/proc/self/ns/net", &us);
182 if (r < 0)
183 return sd_bus_error_set_errno(error, r);
184
185 p = procfs_file_alloca(m->leader, "ns/net");
186 r = readlink_malloc(p, &them);
187 if (r < 0)
188 return sd_bus_error_set_errno(error, r);
189
190 if (streq(us, them))
191 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
192
193 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
194 if (r < 0)
195 return sd_bus_error_set_errno(error, r);
196
197 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
198 return sd_bus_error_set_errno(error, -errno);
199
200 child = fork();
201 if (child < 0)
202 return sd_bus_error_set_errno(error, -errno);
203
204 if (child == 0) {
205 _cleanup_free_ struct local_address *addresses = NULL;
206 struct local_address *a;
207 int i, n;
208
209 pair[0] = safe_close(pair[0]);
210
211 r = namespace_enter(-1, -1, netns_fd, -1);
212 if (r < 0)
213 _exit(EXIT_FAILURE);
214
215 n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
216 if (n < 0)
217 _exit(EXIT_FAILURE);
218
219 for (a = addresses, i = 0; i < n; a++, i++) {
220 struct iovec iov[2] = {
221 { .iov_base = &a->family, .iov_len = sizeof(a->family) },
222 { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
223 };
224
225 r = writev(pair[1], iov, 2);
226 if (r < 0)
227 _exit(EXIT_FAILURE);
228 }
229
230 pair[1] = safe_close(pair[1]);
231
232 _exit(EXIT_SUCCESS);
233 }
234
235 pair[1] = safe_close(pair[1]);
236
237 r = sd_bus_message_new_method_return(message, &reply);
238 if (r < 0)
239 return sd_bus_error_set_errno(error, r);
240
241 r = sd_bus_message_open_container(reply, 'a', "(iay)");
242 if (r < 0)
243 return sd_bus_error_set_errno(error, r);
244
245 for (;;) {
246 int family;
247 ssize_t n;
248 union in_addr_union in_addr;
249 struct iovec iov[2];
250 struct msghdr mh = {
251 .msg_iov = iov,
252 .msg_iovlen = 2,
253 };
254
255 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
256 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
257
258 n = recvmsg(pair[0], &mh, 0);
259 if (n < 0)
260 return sd_bus_error_set_errno(error, -errno);
261 if ((size_t) n < sizeof(family))
262 break;
263
264 r = sd_bus_message_open_container(reply, 'r', "iay");
265 if (r < 0)
266 return sd_bus_error_set_errno(error, r);
267
268 r = sd_bus_message_append(reply, "i", family);
269 if (r < 0)
270 return sd_bus_error_set_errno(error, r);
271
272 switch (family) {
273
274 case AF_INET:
275 if (n != sizeof(struct in_addr) + sizeof(family))
276 return sd_bus_error_set_errno(error, EIO);
277
278 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
279 break;
280
281 case AF_INET6:
282 if (n != sizeof(struct in6_addr) + sizeof(family))
283 return sd_bus_error_set_errno(error, EIO);
284
285 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
286 break;
287 }
288 if (r < 0)
289 return sd_bus_error_set_errno(error, r);
290
291 r = sd_bus_message_close_container(reply);
292 if (r < 0)
293 return sd_bus_error_set_errno(error, r);
294 }
295
296 r = wait_for_terminate(child, &si);
297 if (r < 0)
298 return sd_bus_error_set_errno(error, r);
299 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
300 return sd_bus_error_set_errno(error, EIO);
301
302 r = sd_bus_message_close_container(reply);
303 if (r < 0)
304 return sd_bus_error_set_errno(error, r);
305
306 return sd_bus_send(bus, reply, NULL);
307 }
308
309 int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
310 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
311 _cleanup_close_ int mntns_fd = -1, root_fd = -1;
312 _cleanup_close_pair_ int pair[2] = { -1, -1 };
313 _cleanup_strv_free_ char **l = NULL;
314 _cleanup_fclose_ FILE *f = NULL;
315 Machine *m = userdata;
316 char **k, **v;
317 siginfo_t si;
318 pid_t child;
319 int r;
320
321 assert(bus);
322 assert(message);
323 assert(m);
324
325 if (m->class != MACHINE_CONTAINER)
326 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
327
328 r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
329 if (r < 0)
330 return r;
331
332 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
333 return -errno;
334
335 child = fork();
336 if (child < 0)
337 return -errno;
338
339 if (child == 0) {
340 _cleanup_close_ int fd = -1;
341
342 pair[0] = safe_close(pair[0]);
343
344 r = namespace_enter(-1, mntns_fd, -1, root_fd);
345 if (r < 0)
346 _exit(EXIT_FAILURE);
347
348 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
349 if (fd < 0) {
350 fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
351 if (fd < 0)
352 _exit(EXIT_FAILURE);
353 }
354
355 r = copy_bytes(fd, pair[1], (off_t) -1, false);
356 if (r < 0)
357 _exit(EXIT_FAILURE);
358
359 _exit(EXIT_SUCCESS);
360 }
361
362 pair[1] = safe_close(pair[1]);
363
364 f = fdopen(pair[0], "re");
365 if (!f)
366 return -errno;
367
368 pair[0] = -1;
369
370 r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
371 if (r < 0)
372 return r;
373
374 r = wait_for_terminate(child, &si);
375 if (r < 0)
376 return r;
377 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
378 return -EIO;
379
380 r = sd_bus_message_new_method_return(message, &reply);
381 if (r < 0)
382 return r;
383
384 r = sd_bus_message_open_container(reply, 'a', "{ss}");
385 if (r < 0)
386 return r;
387
388 STRV_FOREACH_PAIR(k, v, l) {
389 r = sd_bus_message_append(reply, "{ss}", *k, *v);
390 if (r < 0)
391 return r;
392 }
393
394 r = sd_bus_message_close_container(reply);
395 if (r < 0)
396 return r;
397
398 return sd_bus_send(bus, reply, NULL);
399 }
400
401 int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
402 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
403 _cleanup_free_ char *pty_name = NULL;
404 _cleanup_close_ int master = -1;
405 Machine *m = userdata;
406 int r;
407
408 assert(bus);
409 assert(message);
410 assert(m);
411
412 if (m->class != MACHINE_CONTAINER)
413 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening pseudo TTYs is only supported on container machines.");
414
415 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
416 if (master < 0)
417 return master;
418
419 r = ptsname_malloc(master, &pty_name);
420 if (r < 0)
421 return r;
422
423 r = sd_bus_message_new_method_return(message, &reply);
424 if (r < 0)
425 return r;
426
427 r = sd_bus_message_append(reply, "hs", master, pty_name);
428 if (r < 0)
429 return r;
430
431 return sd_bus_send(bus, reply, NULL);
432 }
433
434 int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
435 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
436 _cleanup_free_ char *pty_name = NULL, *getty = NULL;
437 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
438 _cleanup_close_ int master = -1;
439 Machine *m = userdata;
440 const char *p;
441 int r;
442
443 if (m->class != MACHINE_CONTAINER)
444 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines.");
445
446 r = bus_verify_polkit_async(
447 message,
448 CAP_SYS_ADMIN,
449 "org.freedesktop.machine1.login",
450 false,
451 &m->manager->polkit_registry,
452 error);
453 if (r < 0)
454 return r;
455 if (r == 0)
456 return 1; /* Will call us back */
457
458 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
459 if (master < 0)
460 return master;
461
462 r = ptsname_malloc(master, &pty_name);
463 if (r < 0)
464 return r;
465
466 p = path_startswith(pty_name, "/dev/pts/");
467 if (!p)
468 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
469
470 if (unlockpt(master) < 0)
471 return -errno;
472
473 r = sd_bus_new(&container_bus);
474 if (r < 0)
475 return r;
476
477 #ifdef ENABLE_KDBUS
478 asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT ";x-machine-unix:pid=" PID_FMT, m->leader, m->leader);
479 #else
480 asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT, m->leader);
481 #endif
482 if (!container_bus->address)
483 return -ENOMEM;
484
485 container_bus->bus_client = true;
486 container_bus->trusted = false;
487 container_bus->is_system = true;
488
489 r = sd_bus_start(container_bus);
490 if (r < 0)
491 return r;
492
493 getty = strjoin("container-getty@", p, ".service", NULL);
494 if (!getty)
495 return -ENOMEM;
496
497 r = sd_bus_call_method(
498 container_bus,
499 "org.freedesktop.systemd1",
500 "/org/freedesktop/systemd1",
501 "org.freedesktop.systemd1.Manager",
502 "StartUnit",
503 error, NULL,
504 "ss", getty, "replace");
505 if (r < 0)
506 return r;
507
508 container_bus = sd_bus_unref(container_bus);
509
510 r = sd_bus_message_new_method_return(message, &reply);
511 if (r < 0)
512 return r;
513
514 r = sd_bus_message_append(reply, "hs", master, pty_name);
515 if (r < 0)
516 return r;
517
518 return sd_bus_send(bus, reply, NULL);
519 }
520
521 const sd_bus_vtable machine_vtable[] = {
522 SD_BUS_VTABLE_START(0),
523 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
524 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
525 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
526 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
527 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
528 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
529 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
530 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
531 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
532 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
533 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
534 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
535 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
536 SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
537 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
538 SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
539 SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
540 SD_BUS_VTABLE_END
541 };
542
543 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
544 Manager *m = userdata;
545 Machine *machine;
546 int r;
547
548 assert(bus);
549 assert(path);
550 assert(interface);
551 assert(found);
552 assert(m);
553
554 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
555 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
556 sd_bus_message *message;
557 pid_t pid;
558
559 message = sd_bus_get_current_message(bus);
560 if (!message)
561 return 0;
562
563 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
564 if (r < 0)
565 return r;
566
567 r = sd_bus_creds_get_pid(creds, &pid);
568 if (r < 0)
569 return r;
570
571 r = manager_get_machine_by_pid(m, pid, &machine);
572 if (r <= 0)
573 return 0;
574 } else {
575 _cleanup_free_ char *e = NULL;
576 const char *p;
577
578 p = startswith(path, "/org/freedesktop/machine1/machine/");
579 if (!p)
580 return 0;
581
582 e = bus_label_unescape(p);
583 if (!e)
584 return -ENOMEM;
585
586 machine = hashmap_get(m->machines, e);
587 if (!machine)
588 return 0;
589 }
590
591 *found = machine;
592 return 1;
593 }
594
595 char *machine_bus_path(Machine *m) {
596 _cleanup_free_ char *e = NULL;
597
598 assert(m);
599
600 e = bus_label_escape(m->name);
601 if (!e)
602 return NULL;
603
604 return strappend("/org/freedesktop/machine1/machine/", e);
605 }
606
607 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
608 _cleanup_strv_free_ char **l = NULL;
609 Machine *machine = NULL;
610 Manager *m = userdata;
611 Iterator i;
612 int r;
613
614 assert(bus);
615 assert(path);
616 assert(nodes);
617
618 HASHMAP_FOREACH(machine, m->machines, i) {
619 char *p;
620
621 p = machine_bus_path(machine);
622 if (!p)
623 return -ENOMEM;
624
625 r = strv_consume(&l, p);
626 if (r < 0)
627 return r;
628 }
629
630 *nodes = l;
631 l = NULL;
632
633 return 1;
634 }
635
636 int machine_send_signal(Machine *m, bool new_machine) {
637 _cleanup_free_ char *p = NULL;
638
639 assert(m);
640
641 p = machine_bus_path(m);
642 if (!p)
643 return -ENOMEM;
644
645 return sd_bus_emit_signal(
646 m->manager->bus,
647 "/org/freedesktop/machine1",
648 "org.freedesktop.machine1.Manager",
649 new_machine ? "MachineNew" : "MachineRemoved",
650 "so", m->name, p);
651 }
652
653 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
654 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
655 _cleanup_free_ char *p = NULL;
656
657 assert(m);
658
659 if (!m->create_message)
660 return 0;
661
662 c = m->create_message;
663 m->create_message = NULL;
664
665 if (error)
666 return sd_bus_reply_method_error(c, error);
667
668 /* Update the machine state file before we notify the client
669 * about the result. */
670 machine_save(m);
671
672 p = machine_bus_path(m);
673 if (!p)
674 return -ENOMEM;
675
676 return sd_bus_reply_method_return(c, "o", p);
677 }