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