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