]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine-dbus.c
machinectl: show /etc/os-release information of container in status output
[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 "sd-rtnl.h"
28 #include "bus-util.h"
29 #include "bus-label.h"
30 #include "strv.h"
31 #include "rtnl-util.h"
32 #include "bus-errors.h"
33 #include "copy.h"
34 #include "fileio.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 BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
87
88 int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
89 Machine *m = userdata;
90 int r;
91
92 assert(bus);
93 assert(message);
94 assert(m);
95
96 r = machine_stop(m);
97 if (r < 0)
98 return r;
99
100 return sd_bus_reply_method_return(message, NULL);
101 }
102
103 int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
104 Machine *m = userdata;
105 const char *swho;
106 int32_t signo;
107 KillWho who;
108 int r;
109
110 assert(bus);
111 assert(message);
112 assert(m);
113
114 r = sd_bus_message_read(message, "si", &swho, &signo);
115 if (r < 0)
116 return r;
117
118 if (isempty(swho))
119 who = KILL_ALL;
120 else {
121 who = kill_who_from_string(swho);
122 if (who < 0)
123 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
124 }
125
126 if (signo <= 0 || signo >= _NSIG)
127 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
128
129 r = machine_kill(m, who, signo);
130 if (r < 0)
131 return r;
132
133 return sd_bus_reply_method_return(message, NULL);
134 }
135
136 int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
137 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
138 _cleanup_close_pair_ int pair[2] = { -1, -1 };
139 _cleanup_free_ char *us = NULL, *them = NULL;
140 _cleanup_close_ int netns_fd = -1;
141 Machine *m = userdata;
142 const char *p;
143 siginfo_t si;
144 pid_t child;
145 int r;
146
147 assert(bus);
148 assert(message);
149 assert(m);
150
151 r = readlink_malloc("/proc/self/ns/net", &us);
152 if (r < 0)
153 return sd_bus_error_set_errno(error, r);
154
155 p = procfs_file_alloca(m->leader, "ns/net");
156 r = readlink_malloc(p, &them);
157 if (r < 0)
158 return sd_bus_error_set_errno(error, r);
159
160 if (streq(us, them))
161 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
162
163 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
164 if (r < 0)
165 return sd_bus_error_set_errno(error, r);
166
167 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
168 return sd_bus_error_set_errno(error, -errno);
169
170 child = fork();
171 if (child < 0)
172 return sd_bus_error_set_errno(error, -errno);
173
174 if (child == 0) {
175 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *resp = NULL;
176 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
177 sd_rtnl_message *addr;
178
179 pair[0] = safe_close(pair[0]);
180
181 r = namespace_enter(-1, -1, netns_fd, -1);
182 if (r < 0)
183 _exit(EXIT_FAILURE);
184
185 r = sd_rtnl_open(&rtnl, 0);
186 if (r < 0)
187 _exit(EXIT_FAILURE);
188
189 r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC);
190 if (r < 0)
191 _exit(EXIT_FAILURE);
192
193 r = sd_rtnl_message_request_dump(req, true);
194 if (r < 0)
195 _exit(EXIT_FAILURE);
196
197 r = sd_rtnl_call(rtnl, req, 0, &resp);
198 if (r < 0)
199 _exit(EXIT_FAILURE);
200
201 for (addr = resp; addr; addr = sd_rtnl_message_next(addr)) {
202 uint16_t type;
203 unsigned char family;
204 union {
205 struct in_addr in;
206 struct in6_addr in6;
207 } in_addr;
208 struct iovec iov[2];
209
210 r = sd_rtnl_message_get_type(addr, &type);
211 if (r < 0)
212 _exit(EXIT_FAILURE);
213
214 if (type != RTM_NEWADDR)
215 continue;
216
217 r = sd_rtnl_message_addr_get_family(addr, &family);
218 if (r < 0)
219 _exit(EXIT_FAILURE);
220
221 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
222
223 switch (family) {
224
225 case AF_INET:
226
227 r = sd_rtnl_message_read_in_addr(addr, IFA_LOCAL, &in_addr.in);
228 if (r < 0)
229 _exit(EXIT_FAILURE);
230
231 if (in_addr.in.s_addr == htobe32(INADDR_LOOPBACK))
232 continue;
233
234 iov[1] = (struct iovec) { .iov_base = &in_addr.in, .iov_len = sizeof(in_addr.in) };
235 break;
236
237 case AF_INET6:
238
239 r = sd_rtnl_message_read_in6_addr(addr, IFA_ADDRESS, &in_addr.in6);
240 if (r < 0)
241 _exit(EXIT_FAILURE);
242
243 if (IN6_IS_ADDR_LOOPBACK(&in_addr.in6))
244 continue;
245
246 iov[1] = (struct iovec) { .iov_base = &in_addr.in6, .iov_len = sizeof(in_addr.in6) };
247 break;
248
249 default:
250 continue;
251 }
252
253 r = writev(pair[1], iov, 2);
254 if (r < 0)
255 _exit(EXIT_FAILURE);
256 }
257
258 _exit(EXIT_SUCCESS);
259 }
260
261 pair[1] = safe_close(pair[1]);
262
263 r = sd_bus_message_new_method_return(message, &reply);
264 if (r < 0)
265 return sd_bus_error_set_errno(error, r);
266
267 r = sd_bus_message_open_container(reply, 'a', "(yay)");
268 if (r < 0)
269 return sd_bus_error_set_errno(error, r);
270
271 for (;;) {
272 unsigned char family;
273 ssize_t n;
274 union {
275 struct in_addr in;
276 struct in6_addr in6;
277 } in_addr;
278 struct iovec iov[2];
279 struct msghdr mh = {
280 .msg_iov = iov,
281 .msg_iovlen = 2,
282 };
283
284 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
285 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
286
287 n = recvmsg(pair[0], &mh, 0);
288 if (n < 0)
289 return sd_bus_error_set_errno(error, -errno);
290 if ((size_t) n < sizeof(family))
291 break;
292
293 r = sd_bus_message_open_container(reply, 'r', "yay");
294 if (r < 0)
295 return sd_bus_error_set_errno(error, r);
296
297 r = sd_bus_message_append(reply, "y", family);
298 if (r < 0)
299 return sd_bus_error_set_errno(error, r);
300
301 switch (family) {
302
303 case AF_INET:
304 if (n != sizeof(struct in_addr) + sizeof(family))
305 return sd_bus_error_set_errno(error, EIO);
306
307 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
308 break;
309
310 case AF_INET6:
311 if (n != sizeof(struct in6_addr) + sizeof(family))
312 return sd_bus_error_set_errno(error, EIO);
313
314 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
315 break;
316 }
317 if (r < 0)
318 return sd_bus_error_set_errno(error, r);
319
320 r = sd_bus_message_close_container(reply);
321 if (r < 0)
322 return sd_bus_error_set_errno(error, r);
323 }
324
325 r = wait_for_terminate(child, &si);
326 if (r < 0)
327 return sd_bus_error_set_errno(error, r);
328 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
329 return sd_bus_error_set_errno(error, EIO);
330
331 r = sd_bus_message_close_container(reply);
332 if (r < 0)
333 return sd_bus_error_set_errno(error, r);
334
335 return sd_bus_send(bus, reply, NULL);
336 }
337
338 int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
339 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
340 _cleanup_close_ int mntns_fd = -1, root_fd = -1;
341 _cleanup_close_pair_ int pair[2] = { -1, -1 };
342 _cleanup_strv_free_ char **l = NULL;
343 _cleanup_fclose_ FILE *f = NULL;
344 Machine *m = userdata;
345 char **k, **v;
346 siginfo_t si;
347 pid_t child;
348 int r;
349
350 assert(bus);
351 assert(message);
352 assert(m);
353
354 r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
355 if (r < 0)
356 return sd_bus_error_set_errno(error, r);
357
358 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
359 return sd_bus_error_set_errno(error, -errno);
360
361 child = fork();
362 if (child < 0)
363 return sd_bus_error_set_errno(error, -errno);
364
365 if (child == 0) {
366 _cleanup_close_ int fd = -1;
367
368 pair[0] = safe_close(pair[0]);
369
370 r = namespace_enter(-1, mntns_fd, -1, root_fd);
371 if (r < 0)
372 _exit(EXIT_FAILURE);
373
374 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
375 if (fd < 0) {
376 fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
377 if (fd < 0)
378 _exit(EXIT_FAILURE);
379 }
380
381 r = copy_bytes(fd, pair[1], (off_t) -1);
382 if (r < 0)
383 _exit(EXIT_FAILURE);
384
385 _exit(EXIT_SUCCESS);
386 }
387
388 pair[1] = safe_close(pair[1]);
389
390 f = fdopen(pair[0], "re");
391 if (!f)
392 return sd_bus_error_set_errno(error, -errno);
393
394 pair[0] = -1;
395
396 r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
397 if (r < 0)
398 return sd_bus_error_set_errno(error, r);
399
400 r = wait_for_terminate(child, &si);
401 if (r < 0)
402 return sd_bus_error_set_errno(error, r);
403 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
404 return sd_bus_error_set_errno(error, EIO);
405
406 r = sd_bus_message_new_method_return(message, &reply);
407 if (r < 0)
408 return sd_bus_error_set_errno(error, r);
409
410 r = sd_bus_message_open_container(reply, 'a', "{ss}");
411 if (r < 0)
412 return sd_bus_error_set_errno(error, r);
413
414 STRV_FOREACH_PAIR(k, v, l) {
415 r = sd_bus_message_append(reply, "{ss}", *k, *v);
416 if (r < 0)
417 return sd_bus_error_set_errno(error, r);
418 }
419
420 r = sd_bus_message_close_container(reply);
421 if (r < 0)
422 return sd_bus_error_set_errno(error, r);
423
424 return sd_bus_send(bus, reply, NULL);
425 }
426
427 const sd_bus_vtable machine_vtable[] = {
428 SD_BUS_VTABLE_START(0),
429 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
430 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
431 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
432 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
433 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
434 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
435 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
436 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
437 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
438 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
439 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
440 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
441 SD_BUS_METHOD("GetAddresses", NULL, "a(yay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
442 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
443 SD_BUS_VTABLE_END
444 };
445
446 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
447 Manager *m = userdata;
448 Machine *machine;
449 int r;
450
451 assert(bus);
452 assert(path);
453 assert(interface);
454 assert(found);
455 assert(m);
456
457 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
458 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
459 sd_bus_message *message;
460 pid_t pid;
461
462 message = sd_bus_get_current_message(bus);
463 if (!message)
464 return 0;
465
466 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
467 if (r < 0)
468 return r;
469
470 r = sd_bus_creds_get_pid(creds, &pid);
471 if (r < 0)
472 return r;
473
474 r = manager_get_machine_by_pid(m, pid, &machine);
475 if (r <= 0)
476 return 0;
477 } else {
478 _cleanup_free_ char *e = NULL;
479 const char *p;
480
481 p = startswith(path, "/org/freedesktop/machine1/machine/");
482 if (!p)
483 return 0;
484
485 e = bus_label_unescape(p);
486 if (!e)
487 return -ENOMEM;
488
489 machine = hashmap_get(m->machines, e);
490 if (!machine)
491 return 0;
492 }
493
494 *found = machine;
495 return 1;
496 }
497
498 char *machine_bus_path(Machine *m) {
499 _cleanup_free_ char *e = NULL;
500
501 assert(m);
502
503 e = bus_label_escape(m->name);
504 if (!e)
505 return NULL;
506
507 return strappend("/org/freedesktop/machine1/machine/", e);
508 }
509
510 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
511 _cleanup_strv_free_ char **l = NULL;
512 Machine *machine = NULL;
513 Manager *m = userdata;
514 Iterator i;
515 int r;
516
517 assert(bus);
518 assert(path);
519 assert(nodes);
520
521 HASHMAP_FOREACH(machine, m->machines, i) {
522 char *p;
523
524 p = machine_bus_path(machine);
525 if (!p)
526 return -ENOMEM;
527
528 r = strv_consume(&l, p);
529 if (r < 0)
530 return r;
531 }
532
533 *nodes = l;
534 l = NULL;
535
536 return 1;
537 }
538
539 int machine_send_signal(Machine *m, bool new_machine) {
540 _cleanup_free_ char *p = NULL;
541
542 assert(m);
543
544 p = machine_bus_path(m);
545 if (!p)
546 return -ENOMEM;
547
548 return sd_bus_emit_signal(
549 m->manager->bus,
550 "/org/freedesktop/machine1",
551 "org.freedesktop.machine1.Manager",
552 new_machine ? "MachineNew" : "MachineRemoved",
553 "so", m->name, p);
554 }
555
556 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
557 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
558 _cleanup_free_ char *p = NULL;
559
560 assert(m);
561
562 if (!m->create_message)
563 return 0;
564
565 c = m->create_message;
566 m->create_message = NULL;
567
568 if (error)
569 return sd_bus_reply_method_error(c, error);
570
571 /* Update the machine state file before we notify the client
572 * about the result. */
573 machine_save(m);
574
575 p = machine_bus_path(m);
576 if (!p)
577 return -ENOMEM;
578
579 return sd_bus_reply_method_return(c, "o", p);
580 }