chiark / gitweb /
timedated: manage systemd-timesyncd directly instead of lists of alternatives
[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 "sd-rtnl.h"
28 #include "bus-util.h"
29 #include "bus-label.h"
30 #include "strv.h"
31 #include "rtnl-util.h"
32 #include "bus-errors.h"
33 #include "copy.h"
34 #include "fileio.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_rtnl_message_unref_ sd_rtnl_message *req = NULL, *resp = NULL;
176                 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
177                 sd_rtnl_message *addr;
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                 r = sd_rtnl_open(&rtnl, 0);
186                 if (r < 0)
187                         _exit(EXIT_FAILURE);
188
189                 r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC);
190                 if (r < 0)
191                         _exit(EXIT_FAILURE);
192
193                 r = sd_rtnl_message_request_dump(req, true);
194                 if (r < 0)
195                         _exit(EXIT_FAILURE);
196
197                 r = sd_rtnl_call(rtnl, req, 0, &resp);
198                 if (r < 0)
199                         _exit(EXIT_FAILURE);
200
201                 for (addr = resp; addr; addr = sd_rtnl_message_next(addr)) {
202                         uint16_t type;
203                         unsigned char family;
204                         union {
205                                 struct in_addr in;
206                                 struct in6_addr in6;
207                         } in_addr;
208                         struct iovec iov[2];
209
210                         r = sd_rtnl_message_get_type(addr, &type);
211                         if (r < 0)
212                                 _exit(EXIT_FAILURE);
213
214                         if (type != RTM_NEWADDR)
215                                 continue;
216
217                         r = sd_rtnl_message_addr_get_family(addr, &family);
218                         if (r < 0)
219                                 _exit(EXIT_FAILURE);
220
221                         iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
222
223                         switch (family) {
224
225                         case AF_INET:
226
227                                 r = sd_rtnl_message_read_in_addr(addr, IFA_LOCAL, &in_addr.in);
228                                 if (r < 0)
229                                         _exit(EXIT_FAILURE);
230
231                                 if (in_addr.in.s_addr == htobe32(INADDR_LOOPBACK))
232                                         continue;
233
234                                 iov[1] = (struct iovec) { .iov_base = &in_addr.in, .iov_len = sizeof(in_addr.in) };
235                                 break;
236
237                         case AF_INET6:
238
239                                 r = sd_rtnl_message_read_in6_addr(addr, IFA_ADDRESS, &in_addr.in6);
240                                 if (r < 0)
241                                         _exit(EXIT_FAILURE);
242
243                                 if (IN6_IS_ADDR_LOOPBACK(&in_addr.in6))
244                                         continue;
245
246                                 iov[1] = (struct iovec) { .iov_base = &in_addr.in6, .iov_len = sizeof(in_addr.in6) };
247                                 break;
248
249                         default:
250                                 continue;
251                         }
252
253                         r = writev(pair[1], iov, 2);
254                         if (r < 0)
255                                 _exit(EXIT_FAILURE);
256                 }
257
258                 _exit(EXIT_SUCCESS);
259         }
260
261         pair[1] = safe_close(pair[1]);
262
263         r = sd_bus_message_new_method_return(message, &reply);
264         if (r < 0)
265                 return sd_bus_error_set_errno(error, r);
266
267         r = sd_bus_message_open_container(reply, 'a', "(yay)");
268         if (r < 0)
269                 return sd_bus_error_set_errno(error, r);
270
271         for (;;) {
272                 unsigned char family;
273                 ssize_t n;
274                 union {
275                         struct in_addr in;
276                         struct in6_addr in6;
277                 } in_addr;
278                 struct iovec iov[2];
279                 struct msghdr mh = {
280                         .msg_iov = iov,
281                         .msg_iovlen = 2,
282                 };
283
284                 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
285                 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
286
287                 n = recvmsg(pair[0], &mh, 0);
288                 if (n < 0)
289                         return sd_bus_error_set_errno(error, -errno);
290                 if ((size_t) n < sizeof(family))
291                         break;
292
293                 r = sd_bus_message_open_container(reply, 'r', "yay");
294                 if (r < 0)
295                         return sd_bus_error_set_errno(error, r);
296
297                 r = sd_bus_message_append(reply, "y", family);
298                 if (r < 0)
299                         return sd_bus_error_set_errno(error, r);
300
301                 switch (family) {
302
303                 case AF_INET:
304                         if (n != sizeof(struct in_addr) + sizeof(family))
305                                 return sd_bus_error_set_errno(error, EIO);
306
307                         r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
308                         break;
309
310                 case AF_INET6:
311                         if (n != sizeof(struct in6_addr) + sizeof(family))
312                                 return sd_bus_error_set_errno(error, EIO);
313
314                         r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
315                         break;
316                 }
317                 if (r < 0)
318                         return sd_bus_error_set_errno(error, r);
319
320                 r = sd_bus_message_close_container(reply);
321                 if (r < 0)
322                         return sd_bus_error_set_errno(error, r);
323         }
324
325         r = wait_for_terminate(child, &si);
326         if (r < 0)
327                 return sd_bus_error_set_errno(error, r);
328         if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
329                 return sd_bus_error_set_errno(error, EIO);
330
331         r = sd_bus_message_close_container(reply);
332         if (r < 0)
333                 return sd_bus_error_set_errno(error, r);
334
335         return sd_bus_send(bus, reply, NULL);
336 }
337
338 int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
339         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
340         _cleanup_close_ int mntns_fd = -1, root_fd = -1;
341         _cleanup_close_pair_ int pair[2] = { -1, -1 };
342         _cleanup_strv_free_ char **l = NULL;
343         _cleanup_fclose_ FILE *f = NULL;
344         Machine *m = userdata;
345         char **k, **v;
346         siginfo_t si;
347         pid_t child;
348         int r;
349
350         assert(bus);
351         assert(message);
352         assert(m);
353
354         r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
355         if (r < 0)
356                 return sd_bus_error_set_errno(error, r);
357
358         if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
359                 return sd_bus_error_set_errno(error, -errno);
360
361         child = fork();
362         if (child < 0)
363                 return sd_bus_error_set_errno(error, -errno);
364
365         if (child == 0) {
366                 _cleanup_close_ int fd = -1;
367
368                 pair[0] = safe_close(pair[0]);
369
370                 r = namespace_enter(-1, mntns_fd, -1, root_fd);
371                 if (r < 0)
372                         _exit(EXIT_FAILURE);
373
374                 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
375                 if (fd < 0) {
376                         fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
377                         if (fd < 0)
378                                 _exit(EXIT_FAILURE);
379                 }
380
381                 r = copy_bytes(fd, pair[1], (off_t) -1);
382                 if (r < 0)
383                         _exit(EXIT_FAILURE);
384
385                 _exit(EXIT_SUCCESS);
386         }
387
388         pair[1] = safe_close(pair[1]);
389
390         f = fdopen(pair[0], "re");
391         if (!f)
392                 return sd_bus_error_set_errno(error, -errno);
393
394         pair[0] = -1;
395
396         r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
397         if (r < 0)
398                 return sd_bus_error_set_errno(error, r);
399
400         r = wait_for_terminate(child, &si);
401         if (r < 0)
402                 return sd_bus_error_set_errno(error, r);
403         if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
404                 return sd_bus_error_set_errno(error, EIO);
405
406         r = sd_bus_message_new_method_return(message, &reply);
407         if (r < 0)
408                 return sd_bus_error_set_errno(error, r);
409
410         r = sd_bus_message_open_container(reply, 'a', "{ss}");
411         if (r < 0)
412                 return sd_bus_error_set_errno(error, r);
413
414         STRV_FOREACH_PAIR(k, v, l) {
415                 r = sd_bus_message_append(reply, "{ss}", *k, *v);
416                 if (r < 0)
417                         return sd_bus_error_set_errno(error, r);
418         }
419
420         r = sd_bus_message_close_container(reply);
421         if (r < 0)
422                 return sd_bus_error_set_errno(error, r);
423
424         return sd_bus_send(bus, reply, NULL);
425 }
426
427 const sd_bus_vtable machine_vtable[] = {
428         SD_BUS_VTABLE_START(0),
429         SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
430         SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
431         BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
432         SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
433         SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
434         SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
435         SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
436         SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
437         SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
438         SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
439         SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
440         SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
441         SD_BUS_METHOD("GetAddresses", NULL, "a(yay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
442         SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
443         SD_BUS_VTABLE_END
444 };
445
446 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
447         Manager *m = userdata;
448         Machine *machine;
449         int r;
450
451         assert(bus);
452         assert(path);
453         assert(interface);
454         assert(found);
455         assert(m);
456
457         if (streq(path, "/org/freedesktop/machine1/machine/self")) {
458                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
459                 sd_bus_message *message;
460                 pid_t pid;
461
462                 message = sd_bus_get_current_message(bus);
463                 if (!message)
464                         return 0;
465
466                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
467                 if (r < 0)
468                         return r;
469
470                 r = sd_bus_creds_get_pid(creds, &pid);
471                 if (r < 0)
472                         return r;
473
474                 r = manager_get_machine_by_pid(m, pid, &machine);
475                 if (r <= 0)
476                         return 0;
477         } else {
478                 _cleanup_free_ char *e = NULL;
479                 const char *p;
480
481                 p = startswith(path, "/org/freedesktop/machine1/machine/");
482                 if (!p)
483                         return 0;
484
485                 e = bus_label_unescape(p);
486                 if (!e)
487                         return -ENOMEM;
488
489                 machine = hashmap_get(m->machines, e);
490                 if (!machine)
491                         return 0;
492         }
493
494         *found = machine;
495         return 1;
496 }
497
498 char *machine_bus_path(Machine *m) {
499         _cleanup_free_ char *e = NULL;
500
501         assert(m);
502
503         e = bus_label_escape(m->name);
504         if (!e)
505                 return NULL;
506
507         return strappend("/org/freedesktop/machine1/machine/", e);
508 }
509
510 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
511         _cleanup_strv_free_ char **l = NULL;
512         Machine *machine = NULL;
513         Manager *m = userdata;
514         Iterator i;
515         int r;
516
517         assert(bus);
518         assert(path);
519         assert(nodes);
520
521         HASHMAP_FOREACH(machine, m->machines, i) {
522                 char *p;
523
524                 p = machine_bus_path(machine);
525                 if (!p)
526                         return -ENOMEM;
527
528                 r = strv_consume(&l, p);
529                 if (r < 0)
530                         return r;
531         }
532
533         *nodes = l;
534         l = NULL;
535
536         return 1;
537 }
538
539 int machine_send_signal(Machine *m, bool new_machine) {
540         _cleanup_free_ char *p = NULL;
541
542         assert(m);
543
544         p = machine_bus_path(m);
545         if (!p)
546                 return -ENOMEM;
547
548         return sd_bus_emit_signal(
549                         m->manager->bus,
550                         "/org/freedesktop/machine1",
551                         "org.freedesktop.machine1.Manager",
552                         new_machine ? "MachineNew" : "MachineRemoved",
553                         "so", m->name, p);
554 }
555
556 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
557         _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
558         _cleanup_free_ char *p = NULL;
559
560         assert(m);
561
562         if (!m->create_message)
563                 return 0;
564
565         c = m->create_message;
566         m->create_message = NULL;
567
568         if (error)
569                 return sd_bus_reply_method_error(c, error);
570
571         /* Update the machine state file before we notify the client
572          * about the result. */
573         machine_save(m);
574
575         p = machine_bus_path(m);
576         if (!p)
577                 return -ENOMEM;
578
579         return sd_bus_reply_method_return(c, "o", p);
580 }