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