chiark / gitweb /
machinectl: show most recent log output in "machinectl status", too
[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         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         r = bus_verify_polkit_async(
435                         message,
436                         CAP_SYS_ADMIN,
437                         "org.freedesktop.machine1.login",
438                         false,
439                         &m->manager->polkit_registry,
440                         error);
441         if (r < 0)
442                 return r;
443         if (r == 0)
444                 return 1; /* Will call us back */
445
446         master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
447         if (master < 0)
448                 return master;
449
450         r = ptsname_malloc(master, &pty_name);
451         if (r < 0)
452                 return r;
453
454         p = path_startswith(pty_name, "/dev/pts/");
455         if (!p)
456                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
457
458         if (unlockpt(master) < 0)
459                 return -errno;
460
461         r = sd_bus_new(&container_bus);
462         if (r < 0)
463                 return r;
464
465 #ifdef ENABLE_KDBUS
466         asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT ";x-machine-unix:pid=" PID_FMT, m->leader, m->leader);
467 #else
468         asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT, m->leader);
469 #endif
470         if (!container_bus->address)
471                 return -ENOMEM;
472
473         container_bus->bus_client = true;
474         container_bus->trusted = false;
475         container_bus->is_system = true;
476
477         r = sd_bus_start(container_bus);
478         if (r < 0)
479                 return r;
480
481         getty = strjoin("container-getty@", p, ".service", NULL);
482         if (!getty)
483                 return -ENOMEM;
484
485         r = sd_bus_call_method(
486                         container_bus,
487                         "org.freedesktop.systemd1",
488                         "/org/freedesktop/systemd1",
489                         "org.freedesktop.systemd1.Manager",
490                         "StartUnit",
491                         error, NULL,
492                         "ss", getty, "replace");
493         if (r < 0)
494                 return r;
495
496         container_bus = sd_bus_unref(container_bus);
497
498         r = sd_bus_message_new_method_return(message, &reply);
499         if (r < 0)
500                 return r;
501
502         r = sd_bus_message_append(reply, "hs", master, pty_name);
503         if (r < 0)
504                 return r;
505
506         return sd_bus_send(bus, reply, NULL);
507 }
508
509 const sd_bus_vtable machine_vtable[] = {
510         SD_BUS_VTABLE_START(0),
511         SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
512         SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
513         BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
514         SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
515         SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
516         SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
517         SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
518         SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
519         SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
520         SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
521         SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
522         SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
523         SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
524         SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
525         SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
526         SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
527         SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
528         SD_BUS_VTABLE_END
529 };
530
531 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
532         Manager *m = userdata;
533         Machine *machine;
534         int r;
535
536         assert(bus);
537         assert(path);
538         assert(interface);
539         assert(found);
540         assert(m);
541
542         if (streq(path, "/org/freedesktop/machine1/machine/self")) {
543                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
544                 sd_bus_message *message;
545                 pid_t pid;
546
547                 message = sd_bus_get_current_message(bus);
548                 if (!message)
549                         return 0;
550
551                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
552                 if (r < 0)
553                         return r;
554
555                 r = sd_bus_creds_get_pid(creds, &pid);
556                 if (r < 0)
557                         return r;
558
559                 r = manager_get_machine_by_pid(m, pid, &machine);
560                 if (r <= 0)
561                         return 0;
562         } else {
563                 _cleanup_free_ char *e = NULL;
564                 const char *p;
565
566                 p = startswith(path, "/org/freedesktop/machine1/machine/");
567                 if (!p)
568                         return 0;
569
570                 e = bus_label_unescape(p);
571                 if (!e)
572                         return -ENOMEM;
573
574                 machine = hashmap_get(m->machines, e);
575                 if (!machine)
576                         return 0;
577         }
578
579         *found = machine;
580         return 1;
581 }
582
583 char *machine_bus_path(Machine *m) {
584         _cleanup_free_ char *e = NULL;
585
586         assert(m);
587
588         e = bus_label_escape(m->name);
589         if (!e)
590                 return NULL;
591
592         return strappend("/org/freedesktop/machine1/machine/", e);
593 }
594
595 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
596         _cleanup_strv_free_ char **l = NULL;
597         Machine *machine = NULL;
598         Manager *m = userdata;
599         Iterator i;
600         int r;
601
602         assert(bus);
603         assert(path);
604         assert(nodes);
605
606         HASHMAP_FOREACH(machine, m->machines, i) {
607                 char *p;
608
609                 p = machine_bus_path(machine);
610                 if (!p)
611                         return -ENOMEM;
612
613                 r = strv_consume(&l, p);
614                 if (r < 0)
615                         return r;
616         }
617
618         *nodes = l;
619         l = NULL;
620
621         return 1;
622 }
623
624 int machine_send_signal(Machine *m, bool new_machine) {
625         _cleanup_free_ char *p = NULL;
626
627         assert(m);
628
629         p = machine_bus_path(m);
630         if (!p)
631                 return -ENOMEM;
632
633         return sd_bus_emit_signal(
634                         m->manager->bus,
635                         "/org/freedesktop/machine1",
636                         "org.freedesktop.machine1.Manager",
637                         new_machine ? "MachineNew" : "MachineRemoved",
638                         "so", m->name, p);
639 }
640
641 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
642         _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
643         _cleanup_free_ char *p = NULL;
644
645         assert(m);
646
647         if (!m->create_message)
648                 return 0;
649
650         c = m->create_message;
651         m->create_message = NULL;
652
653         if (error)
654                 return sd_bus_reply_method_error(c, error);
655
656         /* Update the machine state file before we notify the client
657          * about the result. */
658         machine_save(m);
659
660         p = machine_bus_path(m);
661         if (!p)
662                 return -ENOMEM;
663
664         return sd_bus_reply_method_return(c, "o", p);
665 }