]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machine-dbus.c
hwdb: update
[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 "machine.h"
32 #include "rtnl-util.h"
33 #include "bus-errors.h"
34
35 static int property_get_id(
36 sd_bus *bus,
37 const char *path,
38 const char *interface,
39 const char *property,
40 sd_bus_message *reply,
41 void *userdata,
42 sd_bus_error *error) {
43
44 Machine *m = userdata;
45 int r;
46
47 assert(bus);
48 assert(reply);
49 assert(m);
50
51 r = sd_bus_message_append_array(reply, 'y', &m->id, 16);
52 if (r < 0)
53 return r;
54
55 return 1;
56 }
57
58 static int property_get_state(
59 sd_bus *bus,
60 const char *path,
61 const char *interface,
62 const char *property,
63 sd_bus_message *reply,
64 void *userdata,
65 sd_bus_error *error) {
66
67 Machine *m = userdata;
68 const char *state;
69 int r;
70
71 assert(bus);
72 assert(reply);
73 assert(m);
74
75 state = machine_state_to_string(machine_get_state(m));
76
77 r = sd_bus_message_append_basic(reply, 's', state);
78 if (r < 0)
79 return r;
80
81 return 1;
82 }
83
84 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
85
86 int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
87 Machine *m = userdata;
88 int r;
89
90 assert(bus);
91 assert(message);
92 assert(m);
93
94 r = machine_stop(m);
95 if (r < 0)
96 return r;
97
98 return sd_bus_reply_method_return(message, NULL);
99 }
100
101 int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
102 Machine *m = userdata;
103 const char *swho;
104 int32_t signo;
105 KillWho who;
106 int r;
107
108 assert(bus);
109 assert(message);
110 assert(m);
111
112 r = sd_bus_message_read(message, "si", &swho, &signo);
113 if (r < 0)
114 return r;
115
116 if (isempty(swho))
117 who = KILL_ALL;
118 else {
119 who = kill_who_from_string(swho);
120 if (who < 0)
121 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
122 }
123
124 if (signo <= 0 || signo >= _NSIG)
125 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
126
127 r = machine_kill(m, who, signo);
128 if (r < 0)
129 return r;
130
131 return sd_bus_reply_method_return(message, NULL);
132 }
133
134 int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
135 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
136 _cleanup_close_pair_ int pair[2] = { -1, -1 };
137 _cleanup_free_ char *us = NULL, *them = NULL;
138 _cleanup_close_ int netns_fd = -1;
139 Machine *m = userdata;
140 const char *p;
141 siginfo_t si;
142 pid_t child;
143 int r;
144
145 assert(bus);
146 assert(message);
147 assert(m);
148
149 r = readlink_malloc("/proc/self/ns/net", &us);
150 if (r < 0)
151 return sd_bus_error_set_errno(error, r);
152
153 p = procfs_file_alloca(m->leader, "ns/net");
154 r = readlink_malloc(p, &them);
155 if (r < 0)
156 return sd_bus_error_set_errno(error, r);
157
158 if (streq(us, them))
159 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
160
161 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
162 if (r < 0)
163 return sd_bus_error_set_errno(error, r);
164
165 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
166 return sd_bus_error_set_errno(error, -errno);
167
168 child = fork();
169 if (child < 0)
170 return sd_bus_error_set_errno(error, -errno);
171
172 if (child == 0) {
173 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *resp = NULL;
174 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
175 sd_rtnl_message *addr;
176
177 pair[0] = safe_close(pair[0]);
178
179 r = namespace_enter(-1, -1, netns_fd, -1);
180 if (r < 0)
181 _exit(EXIT_FAILURE);
182
183 r = sd_rtnl_open(&rtnl, 0);
184 if (r < 0)
185 _exit(EXIT_FAILURE);
186
187 r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC);
188 if (r < 0)
189 _exit(EXIT_FAILURE);
190
191 r = sd_rtnl_message_request_dump(req, true);
192 if (r < 0)
193 _exit(EXIT_FAILURE);
194
195 r = sd_rtnl_call(rtnl, req, 0, &resp);
196 if (r < 0)
197 _exit(EXIT_FAILURE);
198
199 for (addr = resp; addr; addr = sd_rtnl_message_next(addr)) {
200 uint16_t type;
201 unsigned char family;
202 union {
203 struct in_addr in;
204 struct in6_addr in6;
205 } in_addr;
206 struct iovec iov[2];
207
208 r = sd_rtnl_message_get_type(addr, &type);
209 if (r < 0)
210 _exit(EXIT_FAILURE);
211
212 if (type != RTM_NEWADDR)
213 continue;
214
215 r = sd_rtnl_message_addr_get_family(addr, &family);
216 if (r < 0)
217 _exit(EXIT_FAILURE);
218
219 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
220
221 switch (family) {
222
223 case AF_INET:
224
225 r = sd_rtnl_message_read_in_addr(addr, IFA_LOCAL, &in_addr.in);
226 if (r < 0)
227 _exit(EXIT_FAILURE);
228
229 if (in_addr.in.s_addr == htobe32(INADDR_LOOPBACK))
230 continue;
231
232 iov[1] = (struct iovec) { .iov_base = &in_addr.in, .iov_len = sizeof(in_addr.in) };
233 break;
234
235 case AF_INET6:
236
237 r = sd_rtnl_message_read_in6_addr(addr, IFA_ADDRESS, &in_addr.in6);
238 if (r < 0)
239 _exit(EXIT_FAILURE);
240
241 if (IN6_IS_ADDR_LOOPBACK(&in_addr.in6))
242 continue;
243
244 iov[1] = (struct iovec) { .iov_base = &in_addr.in6, .iov_len = sizeof(in_addr.in6) };
245 break;
246
247 default:
248 continue;
249 }
250
251 r = writev(pair[1], iov, 2);
252 if (r < 0)
253 _exit(EXIT_FAILURE);
254 }
255
256 _exit(EXIT_SUCCESS);
257 }
258
259 pair[1] = safe_close(pair[1]);
260
261 r = sd_bus_message_new_method_return(message, &reply);
262 if (r < 0)
263 return sd_bus_error_set_errno(error, r);
264
265 r = sd_bus_message_open_container(reply, 'a', "(yay)");
266 if (r < 0)
267 return sd_bus_error_set_errno(error, r);
268
269 for (;;) {
270 unsigned char family;
271 ssize_t n;
272 union {
273 struct in_addr in;
274 struct in6_addr in6;
275 } in_addr;
276 struct iovec iov[2];
277 struct msghdr mh = {
278 .msg_iov = iov,
279 .msg_iovlen = 2,
280 };
281
282 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
283 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
284
285 n = recvmsg(pair[0], &mh, 0);
286 if (n < 0)
287 return sd_bus_error_set_errno(error, -errno);
288 if ((size_t) n < sizeof(family))
289 break;
290
291 r = sd_bus_message_open_container(reply, 'r', "yay");
292 if (r < 0)
293 return sd_bus_error_set_errno(error, r);
294
295 r = sd_bus_message_append(reply, "y", family);
296 if (r < 0)
297 return sd_bus_error_set_errno(error, r);
298
299 switch (family) {
300
301 case AF_INET:
302 if (n != sizeof(struct in_addr) + sizeof(family))
303 return sd_bus_error_set_errno(error, EIO);
304
305 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
306 break;
307
308 case AF_INET6:
309 if (n != sizeof(struct in6_addr) + sizeof(family))
310 return sd_bus_error_set_errno(error, EIO);
311
312 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
313 break;
314 }
315 if (r < 0)
316 return sd_bus_error_set_errno(error, r);
317
318 r = sd_bus_message_close_container(reply);
319 if (r < 0)
320 return sd_bus_error_set_errno(error, r);
321 }
322
323 r = wait_for_terminate(child, &si);
324 if (r < 0)
325 return sd_bus_error_set_errno(error, r);
326 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
327 return sd_bus_error_set_errno(error, EIO);
328
329 r = sd_bus_message_close_container(reply);
330 if (r < 0)
331 return sd_bus_error_set_errno(error, r);
332
333 return sd_bus_send(bus, reply, NULL);
334 }
335
336 const sd_bus_vtable machine_vtable[] = {
337 SD_BUS_VTABLE_START(0),
338 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
339 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
340 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
341 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
342 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
343 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
344 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
345 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
346 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
347 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
348 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
349 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
350 SD_BUS_METHOD("GetAddresses", NULL, "a(yay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
351 SD_BUS_VTABLE_END
352 };
353
354 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
355 Manager *m = userdata;
356 Machine *machine;
357 int r;
358
359 assert(bus);
360 assert(path);
361 assert(interface);
362 assert(found);
363 assert(m);
364
365 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
366 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
367 sd_bus_message *message;
368 pid_t pid;
369
370 message = sd_bus_get_current_message(bus);
371 if (!message)
372 return 0;
373
374 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
375 if (r < 0)
376 return r;
377
378 r = sd_bus_creds_get_pid(creds, &pid);
379 if (r < 0)
380 return r;
381
382 r = manager_get_machine_by_pid(m, pid, &machine);
383 if (r <= 0)
384 return 0;
385 } else {
386 _cleanup_free_ char *e = NULL;
387 const char *p;
388
389 p = startswith(path, "/org/freedesktop/machine1/machine/");
390 if (!p)
391 return 0;
392
393 e = bus_label_unescape(p);
394 if (!e)
395 return -ENOMEM;
396
397 machine = hashmap_get(m->machines, e);
398 if (!machine)
399 return 0;
400 }
401
402 *found = machine;
403 return 1;
404 }
405
406 char *machine_bus_path(Machine *m) {
407 _cleanup_free_ char *e = NULL;
408
409 assert(m);
410
411 e = bus_label_escape(m->name);
412 if (!e)
413 return NULL;
414
415 return strappend("/org/freedesktop/machine1/machine/", e);
416 }
417
418 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
419 _cleanup_strv_free_ char **l = NULL;
420 Machine *machine = NULL;
421 Manager *m = userdata;
422 Iterator i;
423 int r;
424
425 assert(bus);
426 assert(path);
427 assert(nodes);
428
429 HASHMAP_FOREACH(machine, m->machines, i) {
430 char *p;
431
432 p = machine_bus_path(machine);
433 if (!p)
434 return -ENOMEM;
435
436 r = strv_consume(&l, p);
437 if (r < 0)
438 return r;
439 }
440
441 *nodes = l;
442 l = NULL;
443
444 return 1;
445 }
446
447 int machine_send_signal(Machine *m, bool new_machine) {
448 _cleanup_free_ char *p = NULL;
449
450 assert(m);
451
452 p = machine_bus_path(m);
453 if (!p)
454 return -ENOMEM;
455
456 return sd_bus_emit_signal(
457 m->manager->bus,
458 "/org/freedesktop/machine1",
459 "org.freedesktop.machine1.Manager",
460 new_machine ? "MachineNew" : "MachineRemoved",
461 "so", m->name, p);
462 }
463
464 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
465 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
466 _cleanup_free_ char *p = NULL;
467
468 assert(m);
469
470 if (!m->create_message)
471 return 0;
472
473 c = m->create_message;
474 m->create_message = NULL;
475
476 if (error)
477 return sd_bus_reply_method_error(c, error);
478
479 /* Update the machine state file before we notify the client
480 * about the result. */
481 machine_save(m);
482
483 p = machine_bus_path(m);
484 if (!p)
485 return -ENOMEM;
486
487 return sd_bus_reply_method_return(c, "o", p);
488 }