chiark / gitweb /
machined/machinectl: add logic to show list of available images
[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 "image.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 sd_bus_error_set_errno(error, r);
324
325         if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
326                 return sd_bus_error_set_errno(error, -errno);
327
328         child = fork();
329         if (child < 0)
330                 return sd_bus_error_set_errno(error, -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 sd_bus_error_set_errno(error, -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 sd_bus_error_set_errno(error, r);
366
367         r = wait_for_terminate(child, &si);
368         if (r < 0)
369                 return sd_bus_error_set_errno(error, r);
370         if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
371                 return sd_bus_error_set_errno(error, EIO);
372
373         r = sd_bus_message_new_method_return(message, &reply);
374         if (r < 0)
375                 return sd_bus_error_set_errno(error, r);
376
377         r = sd_bus_message_open_container(reply, 'a', "{ss}");
378         if (r < 0)
379                 return sd_bus_error_set_errno(error, 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 sd_bus_error_set_errno(error, r);
385         }
386
387         r = sd_bus_message_close_container(reply);
388         if (r < 0)
389                 return sd_bus_error_set_errno(error, r);
390
391         return sd_bus_send(bus, reply, NULL);
392 }
393
394 const sd_bus_vtable machine_vtable[] = {
395         SD_BUS_VTABLE_START(0),
396         SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
397         SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
398         BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
399         SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
400         SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
401         SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
402         SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
403         SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
404         SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
405         SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
406         SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
407         SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
408         SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
409         SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
410         SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
411         SD_BUS_VTABLE_END
412 };
413
414 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
415         Manager *m = userdata;
416         Machine *machine;
417         int r;
418
419         assert(bus);
420         assert(path);
421         assert(interface);
422         assert(found);
423         assert(m);
424
425         if (streq(path, "/org/freedesktop/machine1/machine/self")) {
426                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
427                 sd_bus_message *message;
428                 pid_t pid;
429
430                 message = sd_bus_get_current_message(bus);
431                 if (!message)
432                         return 0;
433
434                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
435                 if (r < 0)
436                         return r;
437
438                 r = sd_bus_creds_get_pid(creds, &pid);
439                 if (r < 0)
440                         return r;
441
442                 r = manager_get_machine_by_pid(m, pid, &machine);
443                 if (r <= 0)
444                         return 0;
445         } else {
446                 _cleanup_free_ char *e = NULL;
447                 const char *p;
448
449                 p = startswith(path, "/org/freedesktop/machine1/machine/");
450                 if (!p)
451                         return 0;
452
453                 e = bus_label_unescape(p);
454                 if (!e)
455                         return -ENOMEM;
456
457                 machine = hashmap_get(m->machines, e);
458                 if (!machine)
459                         return 0;
460         }
461
462         *found = machine;
463         return 1;
464 }
465
466 char *machine_bus_path(Machine *m) {
467         _cleanup_free_ char *e = NULL;
468
469         assert(m);
470
471         e = bus_label_escape(m->name);
472         if (!e)
473                 return NULL;
474
475         return strappend("/org/freedesktop/machine1/machine/", e);
476 }
477
478 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
479         _cleanup_(image_hashmap_freep) Hashmap *images = NULL;
480         _cleanup_strv_free_ char **l = NULL;
481         Machine *machine = NULL;
482         Manager *m = userdata;
483         Image *image;
484         Iterator i;
485         int r;
486
487         assert(bus);
488         assert(path);
489         assert(nodes);
490
491         HASHMAP_FOREACH(machine, m->machines, i) {
492                 char *p;
493
494                 p = machine_bus_path(machine);
495                 if (!p)
496                         return -ENOMEM;
497
498                 r = strv_consume(&l, p);
499                 if (r < 0)
500                         return r;
501         }
502
503         images = hashmap_new(&string_hash_ops);
504         if (!images)
505                 return -ENOMEM;
506
507         r = image_discover(images);
508         if (r < 0)
509                 return r;
510
511         HASHMAP_FOREACH(image, images, i) {
512                 char *p;
513
514                 p = image_bus_path(image->name);
515                 if (!p)
516                         return -ENOMEM;
517
518                 r = strv_consume(&l, p);
519                 if (r < 0)
520                         return r;
521         }
522
523         *nodes = l;
524         l = NULL;
525
526         return 1;
527 }
528
529 int machine_send_signal(Machine *m, bool new_machine) {
530         _cleanup_free_ char *p = NULL;
531
532         assert(m);
533
534         p = machine_bus_path(m);
535         if (!p)
536                 return -ENOMEM;
537
538         return sd_bus_emit_signal(
539                         m->manager->bus,
540                         "/org/freedesktop/machine1",
541                         "org.freedesktop.machine1.Manager",
542                         new_machine ? "MachineNew" : "MachineRemoved",
543                         "so", m->name, p);
544 }
545
546 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
547         _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
548         _cleanup_free_ char *p = NULL;
549
550         assert(m);
551
552         if (!m->create_message)
553                 return 0;
554
555         c = m->create_message;
556         m->create_message = NULL;
557
558         if (error)
559                 return sd_bus_reply_method_error(c, error);
560
561         /* Update the machine state file before we notify the client
562          * about the result. */
563         machine_save(m);
564
565         p = machine_bus_path(m);
566         if (!p)
567                 return -ENOMEM;
568
569         return sd_bus_reply_method_return(c, "o", p);
570 }