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