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