chiark / gitweb /
machined: add logic to query IP addresses of containers
[elogind.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 }