chiark / gitweb /
3741485a7b33cdc2602cd6db79c4c1190781c9e5
[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-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 BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
87
88 int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
89         Machine *m = userdata;
90         int r;
91
92         assert(bus);
93         assert(message);
94         assert(m);
95
96         r = machine_stop(m);
97         if (r < 0)
98                 return r;
99
100         return sd_bus_reply_method_return(message, NULL);
101 }
102
103 int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
104         Machine *m = userdata;
105         const char *swho;
106         int32_t signo;
107         KillWho who;
108         int r;
109
110         assert(bus);
111         assert(message);
112         assert(m);
113
114         r = sd_bus_message_read(message, "si", &swho, &signo);
115         if (r < 0)
116                 return r;
117
118         if (isempty(swho))
119                 who = KILL_ALL;
120         else {
121                 who = kill_who_from_string(swho);
122                 if (who < 0)
123                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
124         }
125
126         if (signo <= 0 || signo >= _NSIG)
127                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
128
129         r = machine_kill(m, who, signo);
130         if (r < 0)
131                 return r;
132
133         return sd_bus_reply_method_return(message, NULL);
134 }
135
136 int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
137         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
138         _cleanup_close_pair_ int pair[2] = { -1, -1 };
139         _cleanup_free_ char *us = NULL, *them = NULL;
140         _cleanup_close_ int netns_fd = -1;
141         Machine *m = userdata;
142         const char *p;
143         siginfo_t si;
144         pid_t child;
145         int r;
146
147         assert(bus);
148         assert(message);
149         assert(m);
150
151         r = readlink_malloc("/proc/self/ns/net", &us);
152         if (r < 0)
153                 return sd_bus_error_set_errno(error, r);
154
155         p = procfs_file_alloca(m->leader, "ns/net");
156         r = readlink_malloc(p, &them);
157         if (r < 0)
158                 return sd_bus_error_set_errno(error, r);
159
160         if (streq(us, them))
161                 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
162
163         r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
164         if (r < 0)
165                 return sd_bus_error_set_errno(error, r);
166
167         if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
168                 return sd_bus_error_set_errno(error, -errno);
169
170         child = fork();
171         if (child < 0)
172                 return sd_bus_error_set_errno(error, -errno);
173
174         if (child == 0) {
175                 _cleanup_free_ struct local_address *addresses = NULL;
176                 struct local_address *a;
177                 int i, n;
178
179                 pair[0] = safe_close(pair[0]);
180
181                 r = namespace_enter(-1, -1, netns_fd, -1);
182                 if (r < 0)
183                         _exit(EXIT_FAILURE);
184
185                 n = local_addresses(&addresses);
186                 if (n < 0)
187                         _exit(EXIT_FAILURE);
188
189                 for (a = addresses, i = 0; i < n; a++, i++) {
190                         struct iovec iov[2] = {
191                                 { .iov_base = &a->family, .iov_len = sizeof(a->family) },
192                                 { .iov_base = &a->address, .iov_len = PROTO_ADDRESS_SIZE(a->family) },
193                         };
194
195                         r = writev(pair[1], iov, 2);
196                         if (r < 0)
197                                 _exit(EXIT_FAILURE);
198                 }
199
200                 pair[1] = safe_close(pair[1]);
201
202                 _exit(EXIT_SUCCESS);
203         }
204
205         pair[1] = safe_close(pair[1]);
206
207         r = sd_bus_message_new_method_return(message, &reply);
208         if (r < 0)
209                 return sd_bus_error_set_errno(error, r);
210
211         r = sd_bus_message_open_container(reply, 'a', "(yay)");
212         if (r < 0)
213                 return sd_bus_error_set_errno(error, r);
214
215         for (;;) {
216                 unsigned char family;
217                 ssize_t n;
218                 union in_addr_union in_addr;
219                 struct iovec iov[2];
220                 struct msghdr mh = {
221                         .msg_iov = iov,
222                         .msg_iovlen = 2,
223                 };
224
225                 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
226                 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
227
228                 n = recvmsg(pair[0], &mh, 0);
229                 if (n < 0)
230                         return sd_bus_error_set_errno(error, -errno);
231                 if ((size_t) n < sizeof(family))
232                         break;
233
234                 r = sd_bus_message_open_container(reply, 'r', "yay");
235                 if (r < 0)
236                         return sd_bus_error_set_errno(error, r);
237
238                 r = sd_bus_message_append(reply, "y", family);
239                 if (r < 0)
240                         return sd_bus_error_set_errno(error, r);
241
242                 switch (family) {
243
244                 case AF_INET:
245                         if (n != sizeof(struct in_addr) + sizeof(family))
246                                 return sd_bus_error_set_errno(error, EIO);
247
248                         r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
249                         break;
250
251                 case AF_INET6:
252                         if (n != sizeof(struct in6_addr) + sizeof(family))
253                                 return sd_bus_error_set_errno(error, EIO);
254
255                         r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
256                         break;
257                 }
258                 if (r < 0)
259                         return sd_bus_error_set_errno(error, r);
260
261                 r = sd_bus_message_close_container(reply);
262                 if (r < 0)
263                         return sd_bus_error_set_errno(error, r);
264         }
265
266         r = wait_for_terminate(child, &si);
267         if (r < 0)
268                 return sd_bus_error_set_errno(error, r);
269         if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
270                 return sd_bus_error_set_errno(error, EIO);
271
272         r = sd_bus_message_close_container(reply);
273         if (r < 0)
274                 return sd_bus_error_set_errno(error, r);
275
276         return sd_bus_send(bus, reply, NULL);
277 }
278
279 int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
280         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
281         _cleanup_close_ int mntns_fd = -1, root_fd = -1;
282         _cleanup_close_pair_ int pair[2] = { -1, -1 };
283         _cleanup_strv_free_ char **l = NULL;
284         _cleanup_fclose_ FILE *f = NULL;
285         Machine *m = userdata;
286         char **k, **v;
287         siginfo_t si;
288         pid_t child;
289         int r;
290
291         assert(bus);
292         assert(message);
293         assert(m);
294
295         r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
296         if (r < 0)
297                 return sd_bus_error_set_errno(error, r);
298
299         if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
300                 return sd_bus_error_set_errno(error, -errno);
301
302         child = fork();
303         if (child < 0)
304                 return sd_bus_error_set_errno(error, -errno);
305
306         if (child == 0) {
307                 _cleanup_close_ int fd = -1;
308
309                 pair[0] = safe_close(pair[0]);
310
311                 r = namespace_enter(-1, mntns_fd, -1, root_fd);
312                 if (r < 0)
313                         _exit(EXIT_FAILURE);
314
315                 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
316                 if (fd < 0) {
317                         fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
318                         if (fd < 0)
319                                 _exit(EXIT_FAILURE);
320                 }
321
322                 r = copy_bytes(fd, pair[1], (off_t) -1);
323                 if (r < 0)
324                         _exit(EXIT_FAILURE);
325
326                 _exit(EXIT_SUCCESS);
327         }
328
329         pair[1] = safe_close(pair[1]);
330
331         f = fdopen(pair[0], "re");
332         if (!f)
333                 return sd_bus_error_set_errno(error, -errno);
334
335         pair[0] = -1;
336
337         r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
338         if (r < 0)
339                 return sd_bus_error_set_errno(error, r);
340
341         r = wait_for_terminate(child, &si);
342         if (r < 0)
343                 return sd_bus_error_set_errno(error, r);
344         if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
345                 return sd_bus_error_set_errno(error, EIO);
346
347         r = sd_bus_message_new_method_return(message, &reply);
348         if (r < 0)
349                 return sd_bus_error_set_errno(error, r);
350
351         r = sd_bus_message_open_container(reply, 'a', "{ss}");
352         if (r < 0)
353                 return sd_bus_error_set_errno(error, r);
354
355         STRV_FOREACH_PAIR(k, v, l) {
356                 r = sd_bus_message_append(reply, "{ss}", *k, *v);
357                 if (r < 0)
358                         return sd_bus_error_set_errno(error, r);
359         }
360
361         r = sd_bus_message_close_container(reply);
362         if (r < 0)
363                 return sd_bus_error_set_errno(error, r);
364
365         return sd_bus_send(bus, reply, NULL);
366 }
367
368 const sd_bus_vtable machine_vtable[] = {
369         SD_BUS_VTABLE_START(0),
370         SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
371         SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
372         BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
373         SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
374         SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
375         SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
376         SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
377         SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
378         SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
379         SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
380         SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
381         SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
382         SD_BUS_METHOD("GetAddresses", NULL, "a(yay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
383         SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
384         SD_BUS_VTABLE_END
385 };
386
387 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
388         Manager *m = userdata;
389         Machine *machine;
390         int r;
391
392         assert(bus);
393         assert(path);
394         assert(interface);
395         assert(found);
396         assert(m);
397
398         if (streq(path, "/org/freedesktop/machine1/machine/self")) {
399                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
400                 sd_bus_message *message;
401                 pid_t pid;
402
403                 message = sd_bus_get_current_message(bus);
404                 if (!message)
405                         return 0;
406
407                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
408                 if (r < 0)
409                         return r;
410
411                 r = sd_bus_creds_get_pid(creds, &pid);
412                 if (r < 0)
413                         return r;
414
415                 r = manager_get_machine_by_pid(m, pid, &machine);
416                 if (r <= 0)
417                         return 0;
418         } else {
419                 _cleanup_free_ char *e = NULL;
420                 const char *p;
421
422                 p = startswith(path, "/org/freedesktop/machine1/machine/");
423                 if (!p)
424                         return 0;
425
426                 e = bus_label_unescape(p);
427                 if (!e)
428                         return -ENOMEM;
429
430                 machine = hashmap_get(m->machines, e);
431                 if (!machine)
432                         return 0;
433         }
434
435         *found = machine;
436         return 1;
437 }
438
439 char *machine_bus_path(Machine *m) {
440         _cleanup_free_ char *e = NULL;
441
442         assert(m);
443
444         e = bus_label_escape(m->name);
445         if (!e)
446                 return NULL;
447
448         return strappend("/org/freedesktop/machine1/machine/", e);
449 }
450
451 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
452         _cleanup_strv_free_ char **l = NULL;
453         Machine *machine = NULL;
454         Manager *m = userdata;
455         Iterator i;
456         int r;
457
458         assert(bus);
459         assert(path);
460         assert(nodes);
461
462         HASHMAP_FOREACH(machine, m->machines, i) {
463                 char *p;
464
465                 p = machine_bus_path(machine);
466                 if (!p)
467                         return -ENOMEM;
468
469                 r = strv_consume(&l, p);
470                 if (r < 0)
471                         return r;
472         }
473
474         *nodes = l;
475         l = NULL;
476
477         return 1;
478 }
479
480 int machine_send_signal(Machine *m, bool new_machine) {
481         _cleanup_free_ char *p = NULL;
482
483         assert(m);
484
485         p = machine_bus_path(m);
486         if (!p)
487                 return -ENOMEM;
488
489         return sd_bus_emit_signal(
490                         m->manager->bus,
491                         "/org/freedesktop/machine1",
492                         "org.freedesktop.machine1.Manager",
493                         new_machine ? "MachineNew" : "MachineRemoved",
494                         "so", m->name, p);
495 }
496
497 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
498         _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
499         _cleanup_free_ char *p = NULL;
500
501         assert(m);
502
503         if (!m->create_message)
504                 return 0;
505
506         c = m->create_message;
507         m->create_message = NULL;
508
509         if (error)
510                 return sd_bus_reply_method_error(c, error);
511
512         /* Update the machine state file before we notify the client
513          * about the result. */
514         machine_save(m);
515
516         p = machine_bus_path(m);
517         if (!p)
518                 return -ENOMEM;
519
520         return sd_bus_reply_method_return(c, "o", p);
521 }