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