bus-kernel.c 18.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/

/***
  This file is part of systemd.

  Copyright 2013 Lennart Poettering

  systemd is free software; you can redistribute it and/or modify it
  under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation; either version 2.1 of the License, or
  (at your option) any later version.

  systemd is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/

22 23 24 25
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif

26
#include <fcntl.h>
27
#include <malloc.h>
28 29 30 31 32 33

#include "util.h"

#include "bus-internal.h"
#include "bus-message.h"
#include "bus-kernel.h"
34
#include "bus-bloom.h"
35

36 37
#define KDBUS_ITEM_NEXT(item) \
        (typeof(item))(((uint8_t *)item) + ALIGN8((item)->size))
38

39 40 41 42
#define KDBUS_ITEM_FOREACH(item, head)                                          \
        for (item = (head)->items;                                              \
             (uint8_t *)(item) < (uint8_t *)(head) + (head)->size;              \
             item = KDBUS_ITEM_NEXT(item))
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

static int parse_unique_name(const char *s, uint64_t *id) {
        int r;

        assert(s);
        assert(id);

        if (!startswith(s, ":1."))
                return 0;

        r = safe_atou64(s + 3, id);
        if (r < 0)
                return r;

        return 1;
}

60
static void append_payload_vec(struct kdbus_msg_item **d, const void *p, size_t sz) {
61 62 63 64
        assert(d);
        assert(p);
        assert(sz > 0);

65 66
        *d = ALIGN8_PTR(*d);

67
        (*d)->size = offsetof(struct kdbus_msg_item, vec) + sizeof(struct kdbus_vec);
68 69 70 71
        (*d)->type = KDBUS_MSG_PAYLOAD_VEC;
        (*d)->vec.address = (uint64_t) p;
        (*d)->vec.size = sz;

72
        *d = (struct kdbus_msg_item *) ((uint8_t*) *d + (*d)->size);
73 74
}

75
static void append_destination(struct kdbus_msg_item **d, const char *s, size_t length) {
76
        assert(d);
77
        assert(s);
78

79 80
        *d = ALIGN8_PTR(*d);

81
        (*d)->size = offsetof(struct kdbus_msg_item, str) + length + 1;
82
        (*d)->type = KDBUS_MSG_DST_NAME;
83
        memcpy((*d)->str, s, length + 1);
84

85
        *d = (struct kdbus_msg_item *) ((uint8_t*) *d + (*d)->size);
86 87
}

88
static void* append_bloom(struct kdbus_msg_item **d, size_t length) {
89 90
        void *r;

91 92 93 94
        assert(d);

        *d = ALIGN8_PTR(*d);

95
        (*d)->size = offsetof(struct kdbus_msg_item, data) + length;
96
        (*d)->type = KDBUS_MSG_BLOOM;
97
        r = (*d)->data;
98

99
        *d = (struct kdbus_msg_item *) ((uint8_t*) *d + (*d)->size);
100 101 102 103

        return r;
}

104 105 106 107 108 109 110 111 112 113 114 115 116
static void append_fds(struct kdbus_msg_item **d, const int fds[], unsigned n_fds) {
        assert(d);
        assert(fds);
        assert(n_fds > 0);

        *d = ALIGN8_PTR(*d);
        (*d)->size = offsetof(struct kdbus_msg_item, fds) + sizeof(int) * n_fds;
        (*d)->type = KDBUS_MSG_UNIX_FDS;
        memcpy((*d)->fds, fds, sizeof(int) * n_fds);

        *d = (struct kdbus_msg_item *) ((uint8_t*) *d + (*d)->size);
}

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
static int bus_message_setup_bloom(sd_bus_message *m, void *bloom) {
        unsigned i;
        int r;

        assert(m);
        assert(bloom);

        memset(bloom, 0, BLOOM_SIZE);

        bloom_add_pair(bloom, "message-type", bus_message_type_to_string(m->header->type));

        if (m->interface)
                bloom_add_pair(bloom, "interface", m->interface);
        if (m->member)
                bloom_add_pair(bloom, "member", m->member);
        if (m->path) {
                bloom_add_pair(bloom, "path", m->path);
                bloom_add_prefixes(bloom, "path-slash-prefix", m->path, '/');
        }

        r = sd_bus_message_rewind(m, true);
        if (r < 0)
                return r;

        for (i = 0; i < 64; i++) {
                char type;
                const char *t;
                char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
                char *e;

                r = sd_bus_message_peek_type(m, &type, NULL);
                if (r < 0)
                        return r;

                if (type != SD_BUS_TYPE_STRING &&
                    type != SD_BUS_TYPE_OBJECT_PATH &&
                    type != SD_BUS_TYPE_SIGNATURE)
                        break;

                r = sd_bus_message_read_basic(m, type, &t);
                if (r < 0)
                        return r;

                e = stpcpy(buf, "arg");
                if (i < 10)
                        *(e++) = '0' + i;
                else {
                        *(e++) = '0' + (i / 10);
                        *(e++) = '0' + (i % 10);
                }

                *e = 0;
                bloom_add_pair(bloom, buf, t);

                strcpy(e, "-dot-prefix");
                bloom_add_prefixes(bloom, buf, t, '.');
                strcpy(e, "-slash-prefix");
                bloom_add_prefixes(bloom, buf, t, '/');
        }

        return 0;
178 179
}

180
static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
181
        struct kdbus_msg_item *d;
182 183 184 185 186
        bool well_known;
        uint64_t unique;
        size_t sz, dl;
        int r;

187
        assert(b);
188 189
        assert(m);
        assert(m->sealed);
190 191 192

        if (m->kdbus)
                return 0;
193 194 195 196 197 198 199 200 201 202

        if (m->destination) {
                r = parse_unique_name(m->destination, &unique);
                if (r < 0)
                        return r;

                well_known = r == 0;
        } else
                well_known = false;

203
        sz = offsetof(struct kdbus_msg, items);
204

205
        /* Add in fixed header, fields header and payload */
206
        sz += 3 * ALIGN8(offsetof(struct kdbus_msg_item, vec) + sizeof(struct kdbus_vec));
207

208
        /* Add space for bloom filter */
209
        sz += ALIGN8(offsetof(struct kdbus_msg_item, data) + BLOOM_SIZE);
210

211 212 213
        /* Add in well-known destination header */
        if (well_known) {
                dl = strlen(m->destination);
214
                sz += ALIGN8(offsetof(struct kdbus_msg_item, str) + dl + 1);
215 216
        }

217 218 219 220
        /* Add space for unix fds */
        if (m->n_fds > 0)
                sz += ALIGN8(offsetof(struct kdbus_msg_item, fds) + sizeof(int)*m->n_fds);

221
        m->kdbus = memalign(8, sz);
222 223 224
        if (!m->kdbus)
                return -ENOMEM;

225 226
        memset(m->kdbus, 0, sz);

227 228 229 230 231
        m->kdbus->flags =
                ((m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
                ((m->header->flags & SD_BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
        m->kdbus->dst_id =
                well_known ? 0 :
232
                m->destination ? unique : KDBUS_DST_ID_BROADCAST;
233 234 235 236 237
        m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS1;
        m->kdbus->cookie = m->header->serial;

        m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC;

238
        d = m->kdbus->items;
239 240 241 242 243 244

        if (well_known)
                append_destination(&d, m->destination, dl);

        append_payload_vec(&d, m->header, sizeof(*m->header));

245 246
        if (m->fields)
                append_payload_vec(&d, m->fields, ALIGN8(m->header->fields_size));
247 248 249 250

        if (m->body)
                append_payload_vec(&d, m->body, m->header->body_size);

251 252 253
        if (m->kdbus->dst_id == KDBUS_DST_ID_BROADCAST) {
                void *p;

254 255 256 257 258 259 260
                p = append_bloom(&d, BLOOM_SIZE);
                r = bus_message_setup_bloom(m, p);
                if (r < 0) {
                        free(m->kdbus);
                        m->kdbus = NULL;
                        return -r;
                }
261
        }
262

263 264 265
        if (m->n_fds > 0)
                append_fds(&d, m->fds, m->n_fds);

266
        m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus;
267 268
        assert(m->kdbus->size <= sz);

269 270
        m->free_kdbus = true;

271 272 273 274
        return 0;
}

int bus_kernel_take_fd(sd_bus *b) {
275 276 277 278 279 280 281 282 283 284 285 286
        struct kdbus_cmd_hello hello = {
                .conn_flags =
                        KDBUS_CMD_HELLO_ACCEPT_FD|
                        KDBUS_CMD_HELLO_ACCEPT_MMAP|
                        KDBUS_CMD_HELLO_ATTACH_COMM|
                        KDBUS_CMD_HELLO_ATTACH_EXE|
                        KDBUS_CMD_HELLO_ATTACH_CMDLINE|
                        KDBUS_CMD_HELLO_ATTACH_CGROUP|
                        KDBUS_CMD_HELLO_ATTACH_CAPS|
                        KDBUS_CMD_HELLO_ATTACH_SECLABEL|
                        KDBUS_CMD_HELLO_ATTACH_AUDIT
        };
287 288 289 290
        int r;

        assert(b);

291 292 293
        if (b->is_server)
                return -EINVAL;

294 295 296 297
        r = ioctl(b->input_fd, KDBUS_CMD_HELLO, &hello);
        if (r < 0)
                return -errno;

298 299 300 301 302 303
        /* The higher 32bit of both flags fields are considered
         * 'incompatible flags'. Refuse them all for now. */
        if (hello.bus_flags > 0xFFFFFFFFULL ||
            hello.conn_flags > 0xFFFFFFFFULL)
                return -ENOTSUP;

304 305 306
        if (hello.bloom_size != BLOOM_SIZE)
                return -ENOTSUP;

307 308 309
        if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello.id) < 0)
                return -ENOMEM;

310
        b->is_kernel = true;
311
        b->bus_client = true;
312
        b->can_fds = true;
313 314 315 316 317 318 319 320 321 322 323 324 325 326

        r = bus_start_running(b);
        if (r < 0)
                return r;

        return 1;
}

int bus_kernel_connect(sd_bus *b) {
        assert(b);
        assert(b->input_fd < 0);
        assert(b->output_fd < 0);
        assert(b->kernel);

327 328 329
        if (b->is_server)
                return -EINVAL;

330
        b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
331
        if (b->input_fd < 0)
332 333 334 335 336 337 338 339 340 341 342 343 344 345
                return -errno;

        b->output_fd = b->input_fd;

        return bus_kernel_take_fd(b);
}

int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
        int r;

        assert(bus);
        assert(m);
        assert(bus->state == BUS_RUNNING);

346
        r = bus_message_setup_kmsg(bus, m);
347 348 349 350 351 352 353
        if (r < 0)
                return r;

        r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
        if (r < 0)
                return errno == EAGAIN ? 0 : -errno;

354
        return 1;
355 356 357
}

static void close_kdbus_msg(struct kdbus_msg *k) {
358
        struct kdbus_msg_item *d;
359

360
        KDBUS_ITEM_FOREACH(d, k) {
361 362 363 364

                if (d->type != KDBUS_MSG_UNIX_FDS)
                        continue;

365
                close_many(d->fds, (d->size - offsetof(struct kdbus_msg_item, fds)) / sizeof(int));
366 367 368 369 370
        }
}

static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_message **ret) {
        sd_bus_message *m = NULL;
371
        struct kdbus_msg_item *d;
372 373 374 375
        unsigned n_payload = 0, n_fds = 0;
        _cleanup_free_ int *fds = NULL;
        struct bus_header *h = NULL;
        size_t total, n_bytes = 0, idx = 0;
376
        const char *destination = NULL, *seclabel = NULL;
377 378 379 380 381 382 383 384 385
        int r;

        assert(bus);
        assert(k);
        assert(ret);

        if (k->payload_type != KDBUS_PAYLOAD_DBUS1)
                return 0;

386
        KDBUS_ITEM_FOREACH(d, k) {
387 388
                size_t l;

389
                l = d->size - offsetof(struct kdbus_msg_item, data);
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

                if (d->type == KDBUS_MSG_PAYLOAD) {

                        if (!h) {
                                if (l < sizeof(struct bus_header))
                                        return -EBADMSG;

                                h = (struct bus_header*) d->data;
                        }

                        n_payload++;
                        n_bytes += l;

                } else if (d->type == KDBUS_MSG_UNIX_FDS) {
                        int *f;
                        unsigned j;

                        j = l / sizeof(int);
                        f = realloc(fds, sizeof(int) * (n_fds + j));
                        if (!f)
                                return -ENOMEM;

                        fds = f;
413
                        memcpy(fds + n_fds, d->fds, sizeof(int) * j);
414
                        n_fds += j;
415

416
                } else if (d->type == KDBUS_MSG_DST_NAME)
417
                        destination = d->str;
418 419
                else if (d->type == KDBUS_MSG_SRC_SECLABEL)
                        seclabel = d->str;
420 421 422 423 424 425 426 427 428 429 430 431
        }

        if (!h)
                return -EBADMSG;

        r = bus_header_size(h, &total);
        if (r < 0)
                return r;

        if (n_bytes != total)
                return -EBADMSG;

432
        r = bus_message_from_header(h, sizeof(struct bus_header), fds, n_fds, NULL, seclabel, 0, &m);
433 434 435
        if (r < 0)
                return r;

436
        KDBUS_ITEM_FOREACH(d, k) {
437 438
                size_t l;

439
                l = d->size - offsetof(struct kdbus_msg_item, data);
440

441
                if (d->type == KDBUS_MSG_PAYLOAD) {
442

443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
                        if (idx == sizeof(struct bus_header) &&
                            l == ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)))
                                m->fields = d->data;
                        else if (idx == sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) &&
                                 l == BUS_MESSAGE_BODY_SIZE(m))
                                m->body = d->data;
                        else if (!(idx == 0 && l == sizeof(struct bus_header))) {
                                sd_bus_message_unref(m);
                                return -EBADMSG;
                        }

                        idx += l;
                } else if (d->type == KDBUS_MSG_SRC_CREDS) {
                        m->pid_starttime = d->creds.starttime / NSEC_PER_USEC;
                        m->uid = d->creds.uid;
                        m->gid = d->creds.gid;
                        m->pid = d->creds.pid;
                        m->tid = d->creds.tid;
                        m->uid_valid = m->gid_valid = true;
                } else if (d->type == KDBUS_MSG_TIMESTAMP) {
                        m->realtime = d->timestamp.realtime_ns / NSEC_PER_USEC;
                        m->monotonic = d->timestamp.monotonic_ns / NSEC_PER_USEC;
                } else if (d->type == KDBUS_MSG_SRC_PID_COMM)
                        m->comm = d->str;
                else if (d->type == KDBUS_MSG_SRC_TID_COMM)
                        m->tid_comm = d->str;
                else if (d->type == KDBUS_MSG_SRC_EXE)
                        m->exe = d->str;
471 472 473
                else if (d->type == KDBUS_MSG_SRC_CMDLINE) {
                        m->cmdline = d->str;
                        m->cmdline_length = l;
474 475
                } else if (d->type == KDBUS_MSG_SRC_CGROUP)
                        m->cgroup = d->str;
476 477
                else if (d->type == KDBUS_MSG_SRC_AUDIT)
                        m->audit = &d->audit;
478
                else
479
                        log_debug("Got unknown field from kernel %llu", d->type);
480
        }
481

482 483 484 485 486 487
        r = bus_message_parse_fields(m);
        if (r < 0) {
                sd_bus_message_unref(m);
                return r;
        }

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
        if (k->src_id == KDBUS_SRC_ID_KERNEL)
                m->sender = "org.freedesktop.DBus";
        else {
                snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id);
                m->sender = m->sender_buffer;
        }

        if (!m->destination) {
                if (destination)
                        m->destination = destination;
                else if (k->dst_id != KDBUS_DST_ID_WELL_KNOWN_NAME &&
                         k->dst_id != KDBUS_DST_ID_BROADCAST) {
                        snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id);
                        m->destination = m->destination_buffer;
                }
        }

505 506 507 508 509 510 511 512 513 514 515 516 517
        /* We take possession of the kmsg struct now */
        m->kdbus = k;
        m->free_kdbus = true;
        m->free_fds = true;

        fds = NULL;

        *ret = m;
        return 1;
}

int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) {
        struct kdbus_msg *k;
518
        size_t sz = 1024;
519 520 521 522 523 524 525 526
        int r;

        assert(bus);
        assert(m);

        for (;;) {
                void *q;

527
                q = memalign(8, sz);
528 529 530
                if (!q)
                        return -errno;

531
                free(bus->rbuffer);
532 533 534
                k = bus->rbuffer = q;
                k->size = sz;

535 536 537
                /* Let's tell valgrind that there's really no need to
                 * initialize this fully. This should be removed again
                 * when valgrind learned the kdbus ioctls natively. */
538
#ifdef HAVE_VALGRIND_MEMCHECK_H
539
                VALGRIND_MAKE_MEM_DEFINED(k, sz);
540
#endif
541

542 543 544 545 546 547 548
                r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, bus->rbuffer);
                if (r >= 0)
                        break;

                if (errno == EAGAIN)
                        return 0;

549
                if (errno != ENOBUFS)
550 551 552 553 554 555 556 557 558 559 560
                        return -errno;

                sz *= 2;
        }

        r = bus_kernel_make_message(bus, k, m);
        if (r > 0)
                bus->rbuffer = NULL;
        else
                close_kdbus_msg(k);

561
        return r < 0 ? r : 1;
562 563 564
}

int bus_kernel_create(const char *name, char **s) {
565
        struct kdbus_cmd_bus_make *make;
566
        struct kdbus_cmd_make_item *n, *cg;
567 568 569 570 571 572 573 574 575 576 577 578
        size_t l;
        int fd;
        char *p;

        assert(name);
        assert(s);

        fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
        if (fd < 0)
                return -errno;

        l = strlen(name);
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
        make = alloca0(offsetof(struct kdbus_cmd_bus_make, items) +
                       sizeof(struct kdbus_cmd_make_item) + sizeof(uint64_t) +
                       sizeof(struct kdbus_cmd_make_item) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1);

        cg = make->items;
        cg->type = KDBUS_CMD_MAKE_CGROUP;
        cg->data64[0] = 1;
        cg->size = sizeof(struct kdbus_cmd_make_item) + sizeof(uint64_t);

        n = KDBUS_ITEM_NEXT(cg);
        n->type = KDBUS_CMD_MAKE_NAME;
        sprintf(n->str, "%lu-%s", (unsigned long) getuid(), name);
        n->size = sizeof(struct kdbus_cmd_make_item) + strlen(n->str) + 1;

        make->size = offsetof(struct kdbus_cmd_bus_make, items) + cg->size + n->size;
594 595
        make->flags = KDBUS_ACCESS_WORLD | KDBUS_POLICY_OPEN;
        make->bus_flags = 0;
596 597
        make->bloom_size = BLOOM_SIZE;
        assert_cc(BLOOM_SIZE % 8 == 0);
598

599
        p = strjoin("/dev/kdbus/", n->str, "/bus", NULL);
600 601 602
        if (!p)
                return -ENOMEM;

603
        if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) {
604 605 606 607 608 609 610 611 612 613
                close_nointr_nofail(fd);
                free(p);
                return -errno;
        }

        if (s)
                *s = p;

        return fd;
}