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