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