chiark / gitweb /
c1901384bad13621af992b576e7e51b0590f1818
[elogind.git] / src / libsystemd-bus / bus-kernel.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 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 #ifdef HAVE_VALGRIND_MEMCHECK_H
23 #include <valgrind/memcheck.h>
24 #endif
25
26 #include <fcntl.h>
27
28 #include "util.h"
29
30 #include "bus-internal.h"
31 #include "bus-message.h"
32 #include "bus-kernel.h"
33
34 #define KDBUS_MSG_FOREACH_DATA(d, k)                                    \
35         for ((d) = (k)->data;                                           \
36              (uint8_t*) (d) < (uint8_t*) (k) + (k)->size;               \
37              (d) = (struct kdbus_msg_data*) ((uint8_t*) (d) + ALIGN8((d)->size)))
38
39 static int parse_unique_name(const char *s, uint64_t *id) {
40         int r;
41
42         assert(s);
43         assert(id);
44
45         if (!startswith(s, ":1."))
46                 return 0;
47
48         r = safe_atou64(s + 3, id);
49         if (r < 0)
50                 return r;
51
52         return 1;
53 }
54
55 static void append_payload_vec(struct kdbus_msg_data **d, const void *p, size_t sz) {
56         assert(d);
57         assert(p);
58         assert(sz > 0);
59
60         *d = ALIGN8_PTR(*d);
61
62         (*d)->size = offsetof(struct kdbus_msg_data, vec) + sizeof(struct kdbus_vec);
63         (*d)->type = KDBUS_MSG_PAYLOAD_VEC;
64         (*d)->vec.address = (uint64_t) p;
65         (*d)->vec.size = sz;
66
67         *d = (struct kdbus_msg_data*) ((uint8_t*) *d + (*d)->size);
68 }
69
70 static void append_destination(struct kdbus_msg_data **d, const char *s, size_t length) {
71         assert(d);
72         assert(d);
73
74         *d = ALIGN8_PTR(*d);
75
76         (*d)->size = offsetof(struct kdbus_msg_data, str) + length + 1;
77         (*d)->type = KDBUS_MSG_DST_NAME;
78         memcpy((*d)->str, s, length + 1);
79
80         *d = (struct kdbus_msg_data*) ((uint8_t*) *d + (*d)->size);
81 }
82
83 static int bus_message_setup_kmsg(sd_bus_message *m) {
84         struct kdbus_msg_data *d;
85         bool well_known;
86         uint64_t unique;
87         size_t sz, dl;
88         int r;
89
90         assert(m);
91         assert(m->sealed);
92
93         if (m->kdbus)
94                 return 0;
95
96         if (m->destination) {
97                 r = parse_unique_name(m->destination, &unique);
98                 if (r < 0)
99                         return r;
100
101                 well_known = r == 0;
102         } else
103                 well_known = false;
104
105         sz = offsetof(struct kdbus_msg, data);
106
107         /* Add in fixed header, fields header, fields header padding and payload */
108         sz += 4 * ALIGN8(offsetof(struct kdbus_msg_data, vec) + sizeof(struct kdbus_vec));
109
110         /* Add in well-known destination header */
111         if (well_known) {
112                 dl = strlen(m->destination);
113                 sz += ALIGN8(offsetof(struct kdbus_msg, data) + dl + 1);
114         }
115
116         m->kdbus = aligned_alloc(8, sz);
117         if (!m->kdbus)
118                 return -ENOMEM;
119
120         memset(m->kdbus, 0, sz);
121
122         m->kdbus->flags =
123                 ((m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
124                 ((m->header->flags & SD_BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
125         m->kdbus->dst_id =
126                 well_known ? 0 :
127                 m->destination ? unique : (uint64_t) -1;
128         m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS1;
129         m->kdbus->cookie = m->header->serial;
130
131         m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC;
132
133         d = m->kdbus->data;
134
135         if (well_known)
136                 append_destination(&d, m->destination, dl);
137
138         append_payload_vec(&d, m->header, sizeof(*m->header));
139
140         if (m->fields) {
141                 append_payload_vec(&d, m->fields, m->header->fields_size);
142
143                 if (m->header->fields_size % 8 != 0) {
144                         static const uint8_t padding[7] = {};
145
146                         append_payload_vec(&d, padding, 8 - (m->header->fields_size % 8));
147                 }
148         }
149
150         if (m->body)
151                 append_payload_vec(&d, m->body, m->header->body_size);
152
153         m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus;
154         assert(m->kdbus->size <= sz);
155
156         m->free_kdbus = true;
157
158         return 0;
159 }
160
161 int bus_kernel_take_fd(sd_bus *b) {
162         struct kdbus_cmd_hello hello = {};
163         int r;
164
165         assert(b);
166
167         if (b->is_server)
168                 return -EINVAL;
169
170         r = ioctl(b->input_fd, KDBUS_CMD_HELLO, &hello);
171         if (r < 0)
172                 return -errno;
173
174         if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello.id) < 0)
175                 return -ENOMEM;
176
177         b->is_kernel = true;
178         b->bus_client = true;
179
180         r = bus_start_running(b);
181         if (r < 0)
182                 return r;
183
184         return 1;
185 }
186
187 int bus_kernel_connect(sd_bus *b) {
188         assert(b);
189         assert(b->input_fd < 0);
190         assert(b->output_fd < 0);
191         assert(b->kernel);
192
193         if (b->is_server)
194                 return -EINVAL;
195
196         b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
197         if (b->input_fd < 0)
198                 return -errno;
199
200         b->output_fd = b->input_fd;
201
202         return bus_kernel_take_fd(b);
203 }
204
205 int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
206         int r;
207
208         assert(bus);
209         assert(m);
210         assert(bus->state == BUS_RUNNING);
211
212         r = bus_message_setup_kmsg(m);
213         if (r < 0)
214                 return r;
215
216         r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
217         if (r < 0)
218                 return errno == EAGAIN ? 0 : -errno;
219
220         return 1;
221 }
222
223 static void close_kdbus_msg(struct kdbus_msg *k) {
224         struct kdbus_msg_data *d;
225
226         KDBUS_MSG_FOREACH_DATA(d, k) {
227
228                 if (d->type != KDBUS_MSG_UNIX_FDS)
229                         continue;
230
231                 close_many(d->fds, (d->size - offsetof(struct kdbus_msg_data, fds)) / sizeof(int));
232         }
233 }
234
235 static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_message **ret) {
236         sd_bus_message *m = NULL;
237         struct kdbus_msg_data *d;
238         unsigned n_payload = 0, n_fds = 0;
239         _cleanup_free_ int *fds = NULL;
240         struct bus_header *h = NULL;
241         size_t total, n_bytes = 0, idx = 0;
242         struct kdbus_creds *creds = NULL;
243         uint64_t nsec = 0;
244         const char *destination = NULL;
245         int r;
246
247         assert(bus);
248         assert(k);
249         assert(ret);
250
251         if (k->payload_type != KDBUS_PAYLOAD_DBUS1)
252                 return 0;
253
254         KDBUS_MSG_FOREACH_DATA(d, k) {
255                 size_t l;
256
257                 l = d->size - offsetof(struct kdbus_msg_data, data);
258
259                 if (d->type == KDBUS_MSG_PAYLOAD) {
260
261                         if (!h) {
262                                 if (l < sizeof(struct bus_header))
263                                         return -EBADMSG;
264
265                                 h = (struct bus_header*) d->data;
266                         }
267
268                         n_payload++;
269                         n_bytes += l;
270
271                 } else if (d->type == KDBUS_MSG_UNIX_FDS) {
272                         int *f;
273                         unsigned j;
274
275                         j = l / sizeof(int);
276                         f = realloc(fds, sizeof(int) * (n_fds + j));
277                         if (!f)
278                                 return -ENOMEM;
279
280                         fds = f;
281                         memcpy(fds + n_fds, d->fds, j);
282                         n_fds += j;
283
284                 } else if (d->type == KDBUS_MSG_SRC_CREDS)
285                         creds = &d->creds;
286                 else if (d->type == KDBUS_MSG_TIMESTAMP)
287                         nsec = d->ts_ns;
288                 else if (d->type == KDBUS_MSG_DST_NAME)
289                         destination = d->str;
290         }
291
292         if (!h)
293                 return -EBADMSG;
294
295         r = bus_header_size(h, &total);
296         if (r < 0)
297                 return r;
298
299         if (n_bytes != total)
300                 return -EBADMSG;
301
302         r = bus_message_from_header(h, sizeof(struct bus_header), fds, n_fds, NULL, NULL, 0, &m);
303         if (r < 0)
304                 return r;
305
306         KDBUS_MSG_FOREACH_DATA(d, k) {
307                 size_t l;
308
309                 if (d->type != KDBUS_MSG_PAYLOAD)
310                         continue;
311
312                 l = d->size - offsetof(struct kdbus_msg_data, data);
313                 if (idx == sizeof(struct bus_header) &&
314                     l == BUS_MESSAGE_FIELDS_SIZE(m))
315                         m->fields = d->data;
316                 else if (idx == sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) &&
317                          l == BUS_MESSAGE_BODY_SIZE(m))
318                         m->body = d->data;
319                 else if (!(idx == 0 && l == sizeof(struct bus_header)) &&
320                          !(idx == sizeof(struct bus_header) + BUS_MESSAGE_FIELDS_SIZE(m))) {
321                         sd_bus_message_unref(m);
322                         return -EBADMSG;
323                 }
324
325                 idx += l;
326         }
327
328         if (creds) {
329                 m->pid_starttime = creds->starttime / NSEC_PER_USEC;
330                 m->uid = creds->uid;
331                 m->gid = creds->gid;
332                 m->pid = creds->pid;
333                 m->tid = creds->tid;
334                 m->uid_valid = m->gid_valid = true;
335         }
336
337         m->timestamp = nsec / NSEC_PER_USEC;
338
339         r = bus_message_parse_fields(m);
340         if (r < 0) {
341                 sd_bus_message_unref(m);
342                 return r;
343         }
344
345         if (k->src_id == KDBUS_SRC_ID_KERNEL)
346                 m->sender = "org.freedesktop.DBus";
347         else {
348                 snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id);
349                 m->sender = m->sender_buffer;
350         }
351
352         if (!m->destination) {
353                 if (destination)
354                         m->destination = destination;
355                 else if (k->dst_id != KDBUS_DST_ID_WELL_KNOWN_NAME &&
356                          k->dst_id != KDBUS_DST_ID_BROADCAST) {
357                         snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id);
358                         m->destination = m->destination_buffer;
359                 }
360         }
361
362         /* We take possession of the kmsg struct now */
363         m->kdbus = k;
364         m->free_kdbus = true;
365         m->free_fds = true;
366
367         fds = NULL;
368
369         *ret = m;
370         return 1;
371 }
372
373 int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) {
374         struct kdbus_msg *k;
375         size_t sz = 1024;
376         int r;
377
378         assert(bus);
379         assert(m);
380
381         for (;;) {
382                 void *q;
383
384                 q = aligned_alloc(8, sz);
385                 if (!q)
386                         return -errno;
387
388                 free(bus->rbuffer);
389                 k = bus->rbuffer = q;
390                 k->size = sz;
391
392                 /* Let's tell valgrind that there's really no need to
393                  * initialize this fully. This should be removed again
394                  * when valgrind learned the kdbus ioctls natively. */
395 #ifdef HAVE_VALGRIND_MEMCHECK_H
396                 VALGRIND_MAKE_MEM_DEFINED(k, sz);
397 #endif
398
399                 r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, bus->rbuffer);
400                 if (r >= 0)
401                         break;
402
403                 if (errno == EAGAIN)
404                         return 0;
405
406                 if (errno != ENOBUFS)
407                         return -errno;
408
409                 sz *= 2;
410         }
411
412         r = bus_kernel_make_message(bus, k, m);
413         if (r > 0)
414                 bus->rbuffer = NULL;
415         else
416                 close_kdbus_msg(k);
417
418         return r < 0 ? r : 1;
419 }
420
421 int bus_kernel_create(const char *name, char **s) {
422         struct kdbus_cmd_fname *fname;
423         size_t l;
424         int fd;
425         char *p;
426
427         assert(name);
428         assert(s);
429
430         fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
431         if (fd < 0)
432                 return -errno;
433
434         l = strlen(name);
435         fname = alloca(offsetof(struct kdbus_cmd_fname, name) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1);
436         sprintf(fname->name, "%lu-%s", (unsigned long) getuid(), name);
437         fname->size = offsetof(struct kdbus_cmd_fname, name) + strlen(fname->name) + 1;
438         fname->kernel_flags = KDBUS_CMD_FNAME_ACCESS_WORLD | KDBUS_CMD_FNAME_POLICY_OPEN;
439         fname->user_flags = 0;
440
441         p = strjoin("/dev/kdbus/", fname->name, "/bus", NULL);
442         if (!p)
443                 return -ENOMEM;
444
445         if (ioctl(fd, KDBUS_CMD_BUS_MAKE, fname) < 0) {
446                 close_nointr_nofail(fd);
447                 free(p);
448                 return -errno;
449         }
450
451         if (s)
452                 *s = p;
453
454         return fd;
455 }