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