]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine-dbus.c
machined: Move image discovery logic into src/shared, so that we can make use of...
[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 r = readlink_malloc("/proc/self/ns/net", &us);
179 if (r < 0)
180 return sd_bus_error_set_errno(error, r);
181
182 p = procfs_file_alloca(m->leader, "ns/net");
183 r = readlink_malloc(p, &them);
184 if (r < 0)
185 return sd_bus_error_set_errno(error, r);
186
187 if (streq(us, them))
188 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
189
190 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
191 if (r < 0)
192 return sd_bus_error_set_errno(error, r);
193
194 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
195 return sd_bus_error_set_errno(error, -errno);
196
197 child = fork();
198 if (child < 0)
199 return sd_bus_error_set_errno(error, -errno);
200
201 if (child == 0) {
202 _cleanup_free_ struct local_address *addresses = NULL;
203 struct local_address *a;
204 int i, n;
205
206 pair[0] = safe_close(pair[0]);
207
208 r = namespace_enter(-1, -1, netns_fd, -1);
209 if (r < 0)
210 _exit(EXIT_FAILURE);
211
212 n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
213 if (n < 0)
214 _exit(EXIT_FAILURE);
215
216 for (a = addresses, i = 0; i < n; a++, i++) {
217 struct iovec iov[2] = {
218 { .iov_base = &a->family, .iov_len = sizeof(a->family) },
219 { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
220 };
221
222 r = writev(pair[1], iov, 2);
223 if (r < 0)
224 _exit(EXIT_FAILURE);
225 }
226
227 pair[1] = safe_close(pair[1]);
228
229 _exit(EXIT_SUCCESS);
230 }
231
232 pair[1] = safe_close(pair[1]);
233
234 r = sd_bus_message_new_method_return(message, &reply);
235 if (r < 0)
236 return sd_bus_error_set_errno(error, r);
237
238 r = sd_bus_message_open_container(reply, 'a', "(iay)");
239 if (r < 0)
240 return sd_bus_error_set_errno(error, r);
241
242 for (;;) {
243 int family;
244 ssize_t n;
245 union in_addr_union in_addr;
246 struct iovec iov[2];
247 struct msghdr mh = {
248 .msg_iov = iov,
249 .msg_iovlen = 2,
250 };
251
252 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
253 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
254
255 n = recvmsg(pair[0], &mh, 0);
256 if (n < 0)
257 return sd_bus_error_set_errno(error, -errno);
258 if ((size_t) n < sizeof(family))
259 break;
260
261 r = sd_bus_message_open_container(reply, 'r', "iay");
262 if (r < 0)
263 return sd_bus_error_set_errno(error, r);
264
265 r = sd_bus_message_append(reply, "i", family);
266 if (r < 0)
267 return sd_bus_error_set_errno(error, r);
268
269 switch (family) {
270
271 case AF_INET:
272 if (n != sizeof(struct in_addr) + sizeof(family))
273 return sd_bus_error_set_errno(error, EIO);
274
275 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
276 break;
277
278 case AF_INET6:
279 if (n != sizeof(struct in6_addr) + sizeof(family))
280 return sd_bus_error_set_errno(error, EIO);
281
282 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
283 break;
284 }
285 if (r < 0)
286 return sd_bus_error_set_errno(error, r);
287
288 r = sd_bus_message_close_container(reply);
289 if (r < 0)
290 return sd_bus_error_set_errno(error, r);
291 }
292
293 r = wait_for_terminate(child, &si);
294 if (r < 0)
295 return sd_bus_error_set_errno(error, r);
296 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
297 return sd_bus_error_set_errno(error, EIO);
298
299 r = sd_bus_message_close_container(reply);
300 if (r < 0)
301 return sd_bus_error_set_errno(error, r);
302
303 return sd_bus_send(bus, reply, NULL);
304 }
305
306 int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
307 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
308 _cleanup_close_ int mntns_fd = -1, root_fd = -1;
309 _cleanup_close_pair_ int pair[2] = { -1, -1 };
310 _cleanup_strv_free_ char **l = NULL;
311 _cleanup_fclose_ FILE *f = NULL;
312 Machine *m = userdata;
313 char **k, **v;
314 siginfo_t si;
315 pid_t child;
316 int r;
317
318 assert(bus);
319 assert(message);
320 assert(m);
321
322 r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
323 if (r < 0)
324 return r;
325
326 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
327 return -errno;
328
329 child = fork();
330 if (child < 0)
331 return -errno;
332
333 if (child == 0) {
334 _cleanup_close_ int fd = -1;
335
336 pair[0] = safe_close(pair[0]);
337
338 r = namespace_enter(-1, mntns_fd, -1, root_fd);
339 if (r < 0)
340 _exit(EXIT_FAILURE);
341
342 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
343 if (fd < 0) {
344 fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
345 if (fd < 0)
346 _exit(EXIT_FAILURE);
347 }
348
349 r = copy_bytes(fd, pair[1], (off_t) -1, false);
350 if (r < 0)
351 _exit(EXIT_FAILURE);
352
353 _exit(EXIT_SUCCESS);
354 }
355
356 pair[1] = safe_close(pair[1]);
357
358 f = fdopen(pair[0], "re");
359 if (!f)
360 return -errno;
361
362 pair[0] = -1;
363
364 r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
365 if (r < 0)
366 return r;
367
368 r = wait_for_terminate(child, &si);
369 if (r < 0)
370 return r;
371 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
372 return -EIO;
373
374 r = sd_bus_message_new_method_return(message, &reply);
375 if (r < 0)
376 return r;
377
378 r = sd_bus_message_open_container(reply, 'a', "{ss}");
379 if (r < 0)
380 return r;
381
382 STRV_FOREACH_PAIR(k, v, l) {
383 r = sd_bus_message_append(reply, "{ss}", *k, *v);
384 if (r < 0)
385 return r;
386 }
387
388 r = sd_bus_message_close_container(reply);
389 if (r < 0)
390 return r;
391
392 return sd_bus_send(bus, reply, NULL);
393 }
394
395 int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
396 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
397 _cleanup_free_ char *pty_name = NULL;
398 _cleanup_close_ int master = -1;
399 Machine *m = userdata;
400 int r;
401
402 assert(bus);
403 assert(message);
404 assert(m);
405
406 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
407 if (master < 0)
408 return master;
409
410 r = ptsname_malloc(master, &pty_name);
411 if (r < 0)
412 return r;
413
414 r = sd_bus_message_new_method_return(message, &reply);
415 if (r < 0)
416 return r;
417
418 r = sd_bus_message_append(reply, "hs", master, pty_name);
419 if (r < 0)
420 return r;
421
422 return sd_bus_send(bus, reply, NULL);
423 }
424
425 int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
426 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
427 _cleanup_free_ char *pty_name = NULL, *getty = NULL;
428 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
429 _cleanup_close_ int master = -1;
430 Machine *m = userdata;
431 const char *p;
432 int r;
433
434 r = bus_verify_polkit_async(
435 message,
436 CAP_SYS_ADMIN,
437 "org.freedesktop.machine1.login",
438 false,
439 &m->manager->polkit_registry,
440 error);
441 if (r < 0)
442 return r;
443 if (r == 0)
444 return 1; /* Will call us back */
445
446 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
447 if (master < 0)
448 return master;
449
450 r = ptsname_malloc(master, &pty_name);
451 if (r < 0)
452 return r;
453
454 p = path_startswith(pty_name, "/dev/pts/");
455 if (!p)
456 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
457
458 if (unlockpt(master) < 0)
459 return -errno;
460
461 r = sd_bus_new(&container_bus);
462 if (r < 0)
463 return r;
464
465 #ifdef ENABLE_KDBUS
466 asprintf(&container_bus->address, "x-container-kernel:pid=" PID_FMT ";x-container-unix:pid=" PID_FMT, m->leader, m->leader);
467 #else
468 asprintf(&container_bus->address, "x-container-kernel:pid=" PID_FMT, m->leader);
469 #endif
470 if (!container_bus->address)
471 return -ENOMEM;
472
473 container_bus->bus_client = true;
474 container_bus->trusted = false;
475 container_bus->is_system = true;
476
477 r = sd_bus_start(container_bus);
478 if (r < 0)
479 return r;
480
481 getty = strjoin("container-getty@", p, ".service", NULL);
482 if (!getty)
483 return -ENOMEM;
484
485 r = sd_bus_call_method(
486 container_bus,
487 "org.freedesktop.systemd1",
488 "/org/freedesktop/systemd1",
489 "org.freedesktop.systemd1.Manager",
490 "StartUnit",
491 error, NULL,
492 "ss", getty, "replace");
493 if (r < 0)
494 return r;
495
496 container_bus = sd_bus_unref(container_bus);
497
498 r = sd_bus_message_new_method_return(message, &reply);
499 if (r < 0)
500 return r;
501
502 r = sd_bus_message_append(reply, "hs", master, pty_name);
503 if (r < 0)
504 return r;
505
506 return sd_bus_send(bus, reply, NULL);
507 }
508
509 const sd_bus_vtable machine_vtable[] = {
510 SD_BUS_VTABLE_START(0),
511 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
512 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
513 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
514 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
515 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
516 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
517 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
518 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
519 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
520 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
521 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
522 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
523 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
524 SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
525 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
526 SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
527 SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
528 SD_BUS_VTABLE_END
529 };
530
531 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
532 Manager *m = userdata;
533 Machine *machine;
534 int r;
535
536 assert(bus);
537 assert(path);
538 assert(interface);
539 assert(found);
540 assert(m);
541
542 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
543 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
544 sd_bus_message *message;
545 pid_t pid;
546
547 message = sd_bus_get_current_message(bus);
548 if (!message)
549 return 0;
550
551 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
552 if (r < 0)
553 return r;
554
555 r = sd_bus_creds_get_pid(creds, &pid);
556 if (r < 0)
557 return r;
558
559 r = manager_get_machine_by_pid(m, pid, &machine);
560 if (r <= 0)
561 return 0;
562 } else {
563 _cleanup_free_ char *e = NULL;
564 const char *p;
565
566 p = startswith(path, "/org/freedesktop/machine1/machine/");
567 if (!p)
568 return 0;
569
570 e = bus_label_unescape(p);
571 if (!e)
572 return -ENOMEM;
573
574 machine = hashmap_get(m->machines, e);
575 if (!machine)
576 return 0;
577 }
578
579 *found = machine;
580 return 1;
581 }
582
583 char *machine_bus_path(Machine *m) {
584 _cleanup_free_ char *e = NULL;
585
586 assert(m);
587
588 e = bus_label_escape(m->name);
589 if (!e)
590 return NULL;
591
592 return strappend("/org/freedesktop/machine1/machine/", e);
593 }
594
595 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
596 _cleanup_strv_free_ char **l = NULL;
597 Machine *machine = NULL;
598 Manager *m = userdata;
599 Iterator i;
600 int r;
601
602 assert(bus);
603 assert(path);
604 assert(nodes);
605
606 HASHMAP_FOREACH(machine, m->machines, i) {
607 char *p;
608
609 p = machine_bus_path(machine);
610 if (!p)
611 return -ENOMEM;
612
613 r = strv_consume(&l, p);
614 if (r < 0)
615 return r;
616 }
617
618 *nodes = l;
619 l = NULL;
620
621 return 1;
622 }
623
624 int machine_send_signal(Machine *m, bool new_machine) {
625 _cleanup_free_ char *p = NULL;
626
627 assert(m);
628
629 p = machine_bus_path(m);
630 if (!p)
631 return -ENOMEM;
632
633 return sd_bus_emit_signal(
634 m->manager->bus,
635 "/org/freedesktop/machine1",
636 "org.freedesktop.machine1.Manager",
637 new_machine ? "MachineNew" : "MachineRemoved",
638 "so", m->name, p);
639 }
640
641 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
642 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
643 _cleanup_free_ char *p = NULL;
644
645 assert(m);
646
647 if (!m->create_message)
648 return 0;
649
650 c = m->create_message;
651 m->create_message = NULL;
652
653 if (error)
654 return sd_bus_reply_method_error(c, error);
655
656 /* Update the machine state file before we notify the client
657 * about the result. */
658 machine_save(m);
659
660 p = machine_bus_path(m);
661 if (!p)
662 return -ENOMEM;
663
664 return sd_bus_reply_method_return(c, "o", p);
665 }