chiark / gitweb /
bus: rework bloom filter logic to operate with variable bloom filter
[elogind.git] / src / libsystemd / sd-bus / bus-kernel.c
index f85b4d5676ff252c0642e0dbd2082dc23d196727..0b5bae32d5fac4b88bcfbe37d8a0402579d5cec6 100644 (file)
@@ -26,6 +26,7 @@
 #include <fcntl.h>
 #include <malloc.h>
 #include <sys/mman.h>
+#include <sys/prctl.h>
 
 #include "util.h"
 #include "strv.h"
@@ -100,20 +101,21 @@ static void append_destination(struct kdbus_item **d, const char *s, size_t leng
         *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
 }
 
-static void* append_bloom(struct kdbus_item **d, size_t length) {
-        void *r;
+static struct kdbus_bloom_filter *append_bloom(struct kdbus_item **d, size_t length) {
+        struct kdbus_item *i;
 
         assert(d);
 
-        *d = ALIGN8_PTR(*d);
+        i = ALIGN8_PTR(*d);
 
-        (*d)->size = offsetof(struct kdbus_item, data) + length;
-        (*d)->type = KDBUS_ITEM_BLOOM;
-        r = (*d)->data;
+        i->size = offsetof(struct kdbus_item, bloom_filter) +
+                  offsetof(struct kdbus_bloom_filter, data) +
+                  length;
+        i->type = KDBUS_ITEM_BLOOM_FILTER;
 
-        *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
+        *d = (struct kdbus_item *) ((uint8_t*) i + i->size);
 
-        return r;
+        return &i->bloom_filter;
 }
 
 static void append_fds(struct kdbus_item **d, const int fds[], unsigned n_fds) {
@@ -129,25 +131,28 @@ static void append_fds(struct kdbus_item **d, const int fds[], unsigned n_fds) {
         *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size);
 }
 
-static int bus_message_setup_bloom(sd_bus_message *m, void *bloom) {
+static int bus_message_setup_bloom(sd_bus_message *m, struct kdbus_bloom_filter *bloom) {
+        void *data;
         unsigned i;
         int r;
 
         assert(m);
         assert(bloom);
 
-        memset(bloom, 0, BLOOM_SIZE);
+        data = bloom->data;
+        memset(data, 0, m->bus->bloom_size);
+        bloom->generation = 0;
 
-        bloom_add_pair(bloom, "message-type", bus_message_type_to_string(m->header->type));
+        bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "message-type", bus_message_type_to_string(m->header->type));
 
         if (m->interface)
-                bloom_add_pair(bloom, "interface", m->interface);
+                bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "interface", m->interface);
         if (m->member)
-                bloom_add_pair(bloom, "member", m->member);
+                bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "member", m->member);
         if (m->path) {
-                bloom_add_pair(bloom, "path", m->path);
-                bloom_add_pair(bloom, "path-slash-prefix", m->path);
-                bloom_add_prefixes(bloom, "path-slash-prefix", m->path, '/');
+                bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path", m->path);
+                bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path-slash-prefix", m->path);
+                bloom_add_prefixes(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path-slash-prefix", m->path, '/');
         }
 
         r = sd_bus_message_rewind(m, true);
@@ -182,12 +187,12 @@ static int bus_message_setup_bloom(sd_bus_message *m, void *bloom) {
                 }
 
                 *e = 0;
-                bloom_add_pair(bloom, buf, t);
+                bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, buf, t);
 
                 strcpy(e, "-dot-prefix");
-                bloom_add_prefixes(bloom, buf, t, '.');
+                bloom_add_prefixes(data, m->bus->bloom_size, m->bus->bloom_n_hash, buf, t, '.');
                 strcpy(e, "-slash-prefix");
-                bloom_add_prefixes(bloom, buf, t, '/');
+                bloom_add_prefixes(data, m->bus->bloom_size, m->bus->bloom_n_hash, buf, t, '/');
         }
 
         return 0;
@@ -206,6 +211,8 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
         assert(m);
         assert(m->sealed);
 
+        /* We put this together only once, if this message is reused
+         * we reuse the earlier-built version */
         if (m->kdbus)
                 return 0;
 
@@ -228,7 +235,9 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
                 ALIGN8(offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec));
 
         /* Add space for bloom filter */
-        sz += ALIGN8(offsetof(struct kdbus_item, data) + BLOOM_SIZE);
+        sz += ALIGN8(offsetof(struct kdbus_item, bloom_filter) +
+                     offsetof(struct kdbus_bloom_filter, data) +
+                     m->bus->bloom_size);
 
         /* Add in well-known destination header */
         if (well_known) {
@@ -257,6 +266,7 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
                 m->destination ? unique : KDBUS_DST_ID_BROADCAST;
         m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS;
         m->kdbus->cookie = m->header->serial;
+        m->kdbus->priority = m->priority;
 
         if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
                 m->kdbus->cookie_reply = m->reply_cookie;
@@ -299,10 +309,10 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
         }
 
         if (m->kdbus->dst_id == KDBUS_DST_ID_BROADCAST) {
-                void *p;
+                struct kdbus_bloom_filter *bloom;
 
-                p = append_bloom(&d, BLOOM_SIZE);
-                r = bus_message_setup_bloom(m, p);
+                bloom = append_bloom(&d, m->bus->bloom_size);
+                r = bus_message_setup_bloom(m, bloom);
                 if (r < 0)
                         goto fail;
         }
@@ -497,8 +507,13 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
                         break;
 
                 case KDBUS_ITEM_TIMESTAMP:
-                        m->realtime = d->timestamp.realtime_ns / NSEC_PER_USEC;
-                        m->monotonic = d->timestamp.monotonic_ns / NSEC_PER_USEC;
+
+                        if (bus->attach_flags & KDBUS_ATTACH_TIMESTAMP) {
+                                m->realtime = d->timestamp.realtime_ns / NSEC_PER_USEC;
+                                m->monotonic = d->timestamp.monotonic_ns / NSEC_PER_USEC;
+                                m->seqnum = d->timestamp.seqnum;
+                        }
+
                         break;
 
                 case KDBUS_ITEM_PID_COMM:
@@ -564,6 +579,11 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
                                 goto fail;
                         break;
 
+                case KDBUS_ITEM_CONN_NAME:
+                        m->creds.conn_name = d->str;
+                        m->creds.mask |= SD_BUS_CREDS_CONNECTION_NAME & bus->creds_mask;
+                        break;
+
                 case KDBUS_ITEM_FDS:
                 case KDBUS_ITEM_SECLABEL:
                         break;
@@ -625,7 +645,9 @@ fail:
 int bus_kernel_take_fd(sd_bus *b) {
         struct kdbus_cmd_hello *hello;
         struct kdbus_item *item;
-        size_t l = 0, sz;
+        _cleanup_free_ char *g = NULL;
+        const char *name;
+        size_t l = 0, m = 0, sz;
         int r;
 
         assert(b);
@@ -635,10 +657,52 @@ int bus_kernel_take_fd(sd_bus *b) {
 
         b->use_memfd = 1;
 
-        sz = ALIGN8(offsetof(struct kdbus_cmd_hello, items));
+        if (b->connection_name) {
+                g = sd_bus_label_escape(b->connection_name);
+                if (!g)
+                        return -ENOMEM;
+
+                name = g;
+        } else {
+                char pr[17] = {};
+
+                /* If no name is explicitly set, we'll include a hint
+                 * indicating the library implementation, a hint which
+                 * kind of bus this is and the thread name */
+
+                assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0);
+
+                if (isempty(pr)) {
+                        name = b->is_system ? "sd-system" :
+                                b->is_user ? "sd-user" : "sd";
+                } else {
+                        _cleanup_free_ char *e = NULL;
+
+                        e = sd_bus_label_escape(pr);
+                        if (!e)
+                                return -ENOMEM;
+
+                        g = strappend(b->is_system ? "sd-system-" :
+                                      b->is_user ? "sd-user-" : "sd-",
+                                      e);
+                        if (!g)
+                                return -ENOMEM;
+
+                        name = g;
+                }
+
+                b->connection_name = sd_bus_label_unescape(name);
+                if (!b->connection_name)
+                        return -ENOMEM;
+        }
+
+        m = strlen(name);
+
+        sz = ALIGN8(offsetof(struct kdbus_cmd_hello, items)) +
+                ALIGN8(offsetof(struct kdbus_item, str) + m + 1);
 
         if (b->fake_creds_valid)
-                sz += ALIGN8(offsetof(struct kdbus_item, creds)) + sizeof(struct kdbus_creds);
+                sz += ALIGN8(offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds));
 
         if (b->fake_label) {
                 l = strlen(b->fake_label);
@@ -653,6 +717,11 @@ int bus_kernel_take_fd(sd_bus *b) {
 
         item = hello->items;
 
+        item->size = offsetof(struct kdbus_item, str) + m + 1;
+        item->type = KDBUS_ITEM_CONN_NAME;
+        memcpy(item->str, name, m + 1);
+        item = KDBUS_ITEM_NEXT(item);
+
         if (b->fake_creds_valid) {
                 item->size = offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds);
                 item->type = KDBUS_ITEM_CREDS;
@@ -663,6 +732,7 @@ int bus_kernel_take_fd(sd_bus *b) {
 
         if (b->fake_label) {
                 item->size = offsetof(struct kdbus_item, str) + l + 1;
+                item->type = KDBUS_ITEM_SECLABEL;
                 memcpy(item->str, b->fake_label, l+1);
         }
 
@@ -684,9 +754,12 @@ int bus_kernel_take_fd(sd_bus *b) {
             hello->conn_flags > 0xFFFFFFFFULL)
                 return -ENOTSUP;
 
-        if (hello->bloom_size != BLOOM_SIZE)
+        if (!bloom_validate_parameters((size_t) hello->bloom.size, (unsigned) hello->bloom.n_hash))
                 return -ENOTSUP;
 
+        b->bloom_size = (size_t) hello->bloom.size;
+        b->bloom_n_hash = (unsigned) hello->bloom.n_hash;
+
         if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello->id) < 0)
                 return -ENOMEM;
 
@@ -722,7 +795,26 @@ int bus_kernel_connect(sd_bus *b) {
         return bus_kernel_take_fd(b);
 }
 
-int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
+static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) {
+        uint64_t off;
+        struct kdbus_item *d;
+
+        assert(bus);
+        assert(k);
+
+        off = (uint8_t *)k - (uint8_t *)bus->kdbus_buffer;
+        ioctl(bus->input_fd, KDBUS_CMD_FREE, &off);
+
+        KDBUS_ITEM_FOREACH(d, k, items) {
+
+                if (d->type == KDBUS_ITEM_FDS)
+                        close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int));
+                else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD)
+                        close_nointr_nofail(d->memfd.fd);
+        }
+}
+
+int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call) {
         int r;
 
         assert(bus);
@@ -738,6 +830,14 @@ int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
         if (r < 0)
                 return r;
 
+        /* If this is a synchronous method call, then let's tell the
+         * kernel, so that it can pass CPU time/scheduling to the
+         * destination for the time, if it wants to. If we
+         * synchronously wait for the result anyway, we won't need CPU
+         * anyway. */
+        if (hint_sync_call)
+                m->kdbus->flags |= KDBUS_MSG_FLAGS_EXPECT_REPLY|KDBUS_MSG_FLAGS_SYNC_REPLY;
+
         r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
         if (r < 0) {
                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -785,29 +885,31 @@ int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
 
                 bus->rqueue[bus->rqueue_size++] = reply;
 
-                return 0;
-        }
-
-        return 1;
-}
-
-static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) {
-        uint64_t off;
-        struct kdbus_item *d;
+        } else if (hint_sync_call) {
+                struct kdbus_msg *k;
 
-        assert(bus);
-        assert(k);
+                k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + m->kdbus->offset_reply);
+                assert(k);
 
-        off = (uint8_t *)k - (uint8_t *)bus->kdbus_buffer;
-        ioctl(bus->input_fd, KDBUS_CMD_FREE, &off);
+                if (k->payload_type == KDBUS_PAYLOAD_DBUS) {
 
-        KDBUS_ITEM_FOREACH(d, k, items) {
+                        r = bus_kernel_make_message(bus, k);
+                        if (r < 0) {
+                                close_kdbus_msg(bus, k);
 
-                if (d->type == KDBUS_ITEM_FDS)
-                        close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int));
-                else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD)
-                        close_nointr_nofail(d->memfd.fd);
+                                /* Anybody can send us invalid messages, let's just drop them. */
+                                if (r == -EBADMSG || r == -EPROTOTYPE)
+                                        log_debug("Ignoring invalid message: %s", strerror(-r));
+                                else
+                                        return r;
+                        }
+                } else {
+                        log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type);
+                        close_kdbus_msg(bus, k);
+                }
         }
+
+        return 1;
 }
 
 static int push_name_owner_changed(sd_bus *bus, const char *name, const char *old_owner, const char *new_owner) {
@@ -946,7 +1048,7 @@ static int bus_kernel_translate_message(sd_bus *bus, struct kdbus_msg *k) {
         return translate[found->type - _KDBUS_ITEM_KERNEL_BASE](bus, k, found);
 }
 
-int bus_kernel_read_message(sd_bus *bus) {
+int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority) {
         struct kdbus_cmd_recv recv = {};
         struct kdbus_msg *k;
         int r;
@@ -957,6 +1059,11 @@ int bus_kernel_read_message(sd_bus *bus) {
         if (r < 0)
                 return r;
 
+        if (hint_priority) {
+                recv.flags |= KDBUS_RECV_USE_PRIORITY;
+                recv.priority = priority;
+        }
+
         r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, &recv);
         if (r < 0) {
                 if (errno == EAGAIN)
@@ -964,8 +1071,8 @@ int bus_kernel_read_message(sd_bus *bus) {
 
                 return -errno;
         }
-        k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + recv.offset);
 
+        k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + recv.offset);
         if (k->payload_type == KDBUS_PAYLOAD_DBUS) {
                 r = bus_kernel_make_message(bus, k);
 
@@ -977,8 +1084,10 @@ int bus_kernel_read_message(sd_bus *bus) {
 
         } else if (k->payload_type == KDBUS_PAYLOAD_KERNEL)
                 r = bus_kernel_translate_message(bus, k);
-        else
+        else {
+                log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type);
                 r = 0;
+        }
 
         if (r <= 0)
                 close_kdbus_msg(bus, k);
@@ -1000,21 +1109,40 @@ int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *al
         assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0);
 
         if (bus->n_memfd_cache <= 0) {
-                struct kdbus_cmd_memfd_make cmd = {
-                        .size = sizeof(struct kdbus_cmd_memfd_make),
-                };
+                _cleanup_free_ char *g = NULL;
+                struct kdbus_cmd_memfd_make *cmd;
+                struct kdbus_item *item;
+                size_t l, sz;
                 int r;
 
                 assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0);
 
-                r = ioctl(bus->input_fd, KDBUS_CMD_MEMFD_NEW, &cmd);
+                assert(bus->connection_name);
+
+                g = sd_bus_label_escape(bus->connection_name);
+                if (!g)
+                        return -ENOMEM;
+
+                l = strlen(g);
+                sz = ALIGN8(offsetof(struct kdbus_cmd_memfd_make, items)) +
+                        ALIGN8(offsetof(struct kdbus_item, str)) +
+                        l + 1;
+                cmd = alloca0(sz);
+                cmd->size = sz;
+
+                item = cmd->items;
+                item->size = ALIGN8(offsetof(struct kdbus_item, str)) + l + 1;
+                item->type = KDBUS_ITEM_MEMFD_NAME;
+                memcpy(item->str, g, l + 1);
+
+                r = ioctl(bus->input_fd, KDBUS_CMD_MEMFD_NEW, cmd);
                 if (r < 0)
                         return -errno;
 
                 *address = NULL;
                 *mapped = 0;
                 *allocated = 0;
-                return cmd.fd;
+                return cmd->fd;
         }
 
         c = &bus->memfd_cache[--bus->n_memfd_cache];
@@ -1136,6 +1264,9 @@ int kdbus_translate_attach_flags(uint64_t mask, uint64_t *kdbus_mask) {
         if (mask & SD_BUS_CREDS_WELL_KNOWN_NAMES)
                 m |= KDBUS_ATTACH_NAMES;
 
+        if (mask & SD_BUS_CREDS_CONNECTION_NAME)
+                m |= KDBUS_ATTACH_CONN_NAME;
+
         *kdbus_mask = m;
         return 0;
 }
@@ -1160,10 +1291,16 @@ int bus_kernel_create_bus(const char *name, bool world, char **s) {
         make->size = offsetof(struct kdbus_cmd_make, items);
 
         n = make->items;
-        n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t);
-        n->type = KDBUS_ITEM_BLOOM_SIZE;
-        n->data64[0] = BLOOM_SIZE;
-        assert_cc(BLOOM_SIZE % 8 == 0);
+        n->size = offsetof(struct kdbus_item, bloom_parameter) +
+                  sizeof(struct kdbus_bloom_parameter);
+        n->type = KDBUS_ITEM_BLOOM_PARAMETER;
+
+        n->bloom_parameter.size = DEFAULT_BLOOM_SIZE;
+        n->bloom_parameter.n_hash = DEFAULT_BLOOM_N_HASH;
+
+        assert_cc(DEFAULT_BLOOM_SIZE > 0);
+        assert_cc(DEFAULT_BLOOM_N_HASH > 0);
+
         make->size += ALIGN8(n->size);
 
         n = KDBUS_ITEM_NEXT(n);
@@ -1243,7 +1380,7 @@ int bus_kernel_create_starter(const char *bus, const char *name) {
                 return -ENOTSUP;
         }
 
-        if (hello->bloom_size != BLOOM_SIZE) {
+        if (!bloom_validate_parameters((size_t) hello->bloom.size, (unsigned) hello->bloom.n_hash)) {
                 close_nointr_nofail(fd);
                 return -ENOTSUP;
         }
@@ -1346,3 +1483,16 @@ int bus_kernel_try_close(sd_bus *bus) {
 
         return 0;
 }
+
+int bus_kernel_drop_one(int fd) {
+        struct kdbus_cmd_recv recv = {
+                .flags = KDBUS_RECV_DROP
+        };
+
+        assert(fd >= 0);
+
+        if (ioctl(fd, KDBUS_CMD_MSG_RECV, &recv) < 0)
+                return -errno;
+
+        return 0;
+}