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