chiark / gitweb /
libsystemd-bus: sd_bus_request_name: use kdbus_translate_request_name_flags()
[elogind.git] / src / libsystemd-bus / bus-control.c
index b222175148222db02ec504d2e4d2a1ed91d576fd..a0eeb305da7d6d57c5f32ccbc73f1367f6014f1b 100644 (file)
 _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
         int r;
 
-        if (!bus)
-                return -EINVAL;
-        if (!unique)
-                return -EINVAL;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(unique, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         r = bus_ensure_running(bus);
         if (r < 0)
@@ -58,16 +55,11 @@ _public_ int sd_bus_request_name(sd_bus *bus, const char *name, int flags) {
         uint32_t ret;
         int r;
 
-        if (!bus)
-                return -EINVAL;
-        if (!name)
-                return -EINVAL;
-        if (!bus->bus_client)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(name, -EINVAL);
+        assert_return(bus->bus_client, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         if (bus->is_kernel) {
                 struct kdbus_cmd_name *n;
@@ -76,7 +68,7 @@ _public_ int sd_bus_request_name(sd_bus *bus, const char *name, int flags) {
                 l = strlen(name);
                 n = alloca0(offsetof(struct kdbus_cmd_name, name) + l + 1);
                 n->size = offsetof(struct kdbus_cmd_name, name) + l + 1;
-                n->flags = flags;
+                kdbus_translate_request_name_flags(flags, (uint64_t *) &n->flags);
                 memcpy(n->name, name, l+1);
 
 #ifdef HAVE_VALGRIND_MEMCHECK_H
@@ -126,16 +118,11 @@ _public_ int sd_bus_release_name(sd_bus *bus, const char *name) {
         uint32_t ret;
         int r;
 
-        if (!bus)
-                return -EINVAL;
-        if (!name)
-                return -EINVAL;
-        if (!bus->bus_client)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(name, -EINVAL);
+        assert_return(bus->bus_client, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         if (bus->is_kernel) {
                 struct kdbus_cmd_name *n;
@@ -181,14 +168,10 @@ _public_ int sd_bus_list_names(sd_bus *bus, char ***l) {
         char **x = NULL;
         int r;
 
-        if (!bus)
-                return -EINVAL;
-        if (!l)
-                return -EINVAL;
-        if (!BUS_IS_OPEN(bus->state))
-                return -ENOTCONN;
-        if (bus_pid_changed(bus))
-                return -ECHILD;
+        assert_return(bus, -EINVAL);
+        assert_return(l, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
 
         if (bus->is_kernel) {
                 _cleanup_free_ struct kdbus_cmd_names *names = NULL;
@@ -277,7 +260,7 @@ _public_ int sd_bus_list_names(sd_bus *bus, char ***l) {
         return 0;
 }
 
-_public_ int sd_bus_get_owner(
+static int sd_bus_get_owner_dbus(
                 sd_bus *bus,
                 const char *name,
                 uint64_t mask,
@@ -290,13 +273,6 @@ _public_ int sd_bus_get_owner(
         pid_t pid = 0;
         int r;
 
-        assert_return(bus, -EINVAL);
-        assert_return(name, -EINVAL);
-        assert_return(mask <= _SD_BUS_CREDS_MAX, -ENOTSUP);
-        assert_return(mask == 0 || creds, -EINVAL);
-        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
-        assert_return(!bus_pid_changed(bus), -ECHILD);
-
         /* Only query the owner if the caller wants to know it or if
          * the caller just wants to check whether a name exists */
         if (owner || mask == 0) {
@@ -433,6 +409,386 @@ _public_ int sd_bus_get_owner(
         return 0;
 }
 
+static int add_name_change_match(sd_bus *bus,
+                                 uint64_t cookie,
+                                 const char *name,
+                                 const char *old_owner,
+                                 const char *new_owner) {
+
+        uint64_t name_id = 0, old_owner_id = 0, new_owner_id = 0;
+        int is_name_id = -1, r;
+        struct kdbus_item *item;
+
+        assert(bus);
+
+        /* If we encounter a match that could match against
+         * NameOwnerChanged messages, then we need to create
+         * KDBUS_MATCH_NAME_{ADD,REMOVE,CHANGE} and
+         * KDBUS_MATCH_ID_{ADD,REMOVE} matches for it, possibly
+         * multiple if the match is underspecified.
+         *
+         * The NameOwnerChanged signals take three parameters with
+         * unique or well-known names, but only some forms actually
+         * exist:
+         *
+         * WELLKNOWN, "", UNIQUE       → KDBUS_MATCH_NAME_ADD
+         * WELLKNOWN, UNIQUE, ""       → KDBUS_MATCH_NAME_REMOVE
+         * WELLKNOWN, UNIQUE, UNIQUE   → KDBUS_MATCH_NAME_CHANGE
+         * UNIQUE, "", UNIQUE          → KDBUS_MATCH_ID_ADD
+         * UNIQUE, UNIQUE, ""          → KDBUS_MATCH_ID_REMOVE
+         *
+         * For the latter two the two unique names must be identical.
+         *
+         * */
+
+        if (name) {
+                is_name_id = bus_kernel_parse_unique_name(name, &name_id);
+                if (is_name_id < 0)
+                        return 0;
+        }
+
+        if (old_owner) {
+                r = bus_kernel_parse_unique_name(old_owner, &old_owner_id);
+                if (r < 0)
+                        return 0;
+                if (r == 0)
+                        return 0;
+                if (is_name_id > 0 && old_owner_id != name_id)
+                        return 0;
+        }
+
+        if (new_owner) {
+                r = bus_kernel_parse_unique_name(new_owner, &new_owner_id);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return 0;
+                if (is_name_id > 0 && new_owner_id != name_id)
+                        return 0;
+        }
+
+        if (is_name_id <= 0) {
+                size_t sz, l;
+
+                /* If the name argument is missing or is a well-known
+                 * name, then add KDBUS_MATCH_NAME_{ADD,REMOVE,CHANGE}
+                 * matches for it */
+
+                l = name ? strlen(name) : 0;
+
+                sz = ALIGN8(offsetof(struct kdbus_cmd_match, items) +
+                            offsetof(struct kdbus_item, name_change) +
+                            offsetof(struct kdbus_notify_name_change, name) +
+                            l+1);
+
+                {
+                        union {
+                                uint8_t buffer[sz];
+                                struct kdbus_cmd_match match;
+                        } m;
+
+                        memzero(&m, sz);
+
+                        m.match.size = sz;
+                        m.match.cookie = cookie;
+                        m.match.src_id = KDBUS_SRC_ID_KERNEL;
+
+                        item = m.match.items;
+                        item->size =
+                                offsetof(struct kdbus_item, name_change) +
+                                offsetof(struct kdbus_notify_name_change, name) +
+                                l+1;
+
+                        item->name_change.old_id = old_owner_id;
+                        item->name_change.new_id = new_owner_id;
+
+                        if (name)
+                                strcpy(item->name_change.name, name);
+
+                        /* If the old name is unset or empty, then
+                         * this can match against added names */
+                        if (!old_owner || old_owner[0] == 0) {
+                                item->type = KDBUS_MATCH_NAME_ADD;
+
+                                r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
+                                if (r < 0)
+                                        return -errno;
+                        }
+
+                        /* If the new name is unset or empty, then
+                         * this can match against removed names */
+                        if (!new_owner || new_owner[0] == 0) {
+                                item->type = KDBUS_MATCH_NAME_REMOVE;
+
+                                r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
+                                if (r < 0)
+                                        return -errno;
+                        }
+
+                        /* If the neither name is explicitly set to
+                         * the empty string, then this can match
+                         * agains changed names */
+                        if (!(old_owner && old_owner[0] == 0) &&
+                            !(new_owner && new_owner[0] == 0)) {
+                                item->type = KDBUS_MATCH_NAME_CHANGE;
+
+                                r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
+                                if (r < 0)
+                                        return -errno;
+                        }
+                }
+        }
+
+        if (is_name_id != 0) {
+                uint64_t sz =
+                        ALIGN8(offsetof(struct kdbus_cmd_match, items) +
+                               offsetof(struct kdbus_item, id_change) +
+                               sizeof(struct kdbus_notify_id_change));
+                union {
+                        uint8_t buffer[sz];
+                        struct kdbus_cmd_match match;
+                } m;
+
+                /* If the name argument is missing or is a unique
+                 * name, then add KDBUS_MATCH_ID_{ADD,REMOVE} matches
+                 * for it */
+
+                memzero(&m, sz);
+
+                m.match.size = sz;
+                m.match.cookie = cookie;
+                m.match.src_id = KDBUS_SRC_ID_KERNEL;
+
+                item = m.match.items;
+                item->size = offsetof(struct kdbus_item, id_change) + sizeof(struct kdbus_notify_id_change);
+                item->id_change.id = name_id;
+
+                /* If the old name is unset or empty, then this can
+                 * match against added ids */
+                if (!old_owner || old_owner[0] == 0) {
+                        item->type = KDBUS_MATCH_ID_ADD;
+
+                        r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
+                        if (r < 0)
+                                return -errno;
+                }
+
+                /* If thew new name is unset or empty, then this can
+                match against removed ids */
+                if (!new_owner || new_owner[0] == 0) {
+                        item->type = KDBUS_MATCH_ID_REMOVE;
+
+                        r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
+                        if (r < 0)
+                                return -errno;
+                }
+        }
+
+        return 0;
+}
+
+static int kdbus_name_query(
+                sd_bus *bus,
+                const char *name,
+                uint64_t mask,
+                char **owner,
+                sd_bus_creds **creds) {
+
+        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
+        _cleanup_free_ char *unique = NULL;
+        struct kdbus_cmd_name_info *name_info = NULL;
+        struct kdbus_item *item;
+        uint64_t attach_flags, m;
+        size_t slen, size;
+        int r;
+
+        r = kdbus_translate_attach_flags(mask, &attach_flags);
+        if (r < 0)
+                return r;
+
+        slen = strlen(name) + 1;
+
+        /*
+         * The structure is used for both directions. Start with 8k buffer size and
+         * expand to the size kdbus reports in case we fail.
+         */
+        size = slen + 8192;
+
+        for(;;) {
+                name_info = realloc(name_info, size);
+                if (!name_info)
+                        return -ENOMEM;
+
+                memset(name_info, 0, size);
+
+                name_info->size = size;
+                name_info->attach_flags = attach_flags;
+
+                item = name_info->items;
+                item->type = KDBUS_NAME_INFO_ITEM_NAME;
+                item->size = KDBUS_ITEM_SIZE(slen);
+                strcpy(item->str, name);
+
+                r = ioctl(sd_bus_get_fd(bus), KDBUS_CMD_NAME_QUERY, name_info);
+                if (r < 0) {
+                        if (errno == ENOBUFS && size != name_info->size) {
+                                size = name_info->size;
+                                continue;
+                        }
+
+                        return -errno;
+                }
+
+                break;
+        }
+
+        asprintf(&unique, ":1.%llu", (unsigned long long) name_info->id);
+
+        c = bus_creds_new();
+        if (!c)
+                return -ENOMEM;
+
+        KDBUS_PART_FOREACH(item, name_info, items) {
+                switch (item->type) {
+                case KDBUS_ITEM_CREDS:
+                        m = (SD_BUS_CREDS_UID | SD_BUS_CREDS_GID | SD_BUS_CREDS_PID |
+                             SD_BUS_CREDS_TID | SD_BUS_CREDS_PID_STARTTIME) & mask;
+
+                        if (m) {
+                                c->uid = item->creds.uid;
+                                c->pid = item->creds.pid;
+                                c->gid = item->creds.gid;
+                                c->tid = item->creds.tid;
+                                c->pid_starttime = item->creds.starttime;
+                                c->mask |= m;
+                        }
+                        break;
+
+                case KDBUS_ITEM_PID_COMM:
+                        if (mask & SD_BUS_CREDS_COMM) {
+                                c->comm = strdup(item->str);
+                                if (!c->comm)
+                                        return -ENOMEM;
+
+                                c->mask |= SD_BUS_CREDS_COMM;
+                        }
+                        break;
+
+                case KDBUS_ITEM_TID_COMM:
+                        if (mask & SD_BUS_CREDS_TID_COMM) {
+                                c->tid_comm = strdup(item->str);
+                                if (!c->tid_comm)
+                                        return -ENOMEM;
+
+                                c->mask |= SD_BUS_CREDS_TID_COMM;
+                        }
+                        break;
+
+                case KDBUS_ITEM_EXE:
+                        if (mask & SD_BUS_CREDS_EXE) {
+                                c->exe = strdup(item->str);
+                                if (!c->exe)
+                                        return -ENOMEM;
+
+                                c->mask |= SD_BUS_CREDS_EXE;
+                        }
+                        break;
+
+                case KDBUS_ITEM_CMDLINE:
+                        if (mask & SD_BUS_CREDS_CMDLINE) {
+                                c->cmdline_length = item->size - KDBUS_PART_HEADER_SIZE;
+                                c->cmdline = memdup(item->data, c->cmdline_length);
+                                if (!c->cmdline)
+                                        return -ENOMEM;
+
+                                c->mask |= SD_BUS_CREDS_CMDLINE;
+                        }
+                        break;
+
+                case KDBUS_ITEM_CGROUP:
+                        m = (SD_BUS_CREDS_CGROUP | SD_BUS_CREDS_UNIT |
+                             SD_BUS_CREDS_USER_UNIT | SD_BUS_CREDS_SLICE |
+                             SD_BUS_CREDS_SESSION | SD_BUS_CREDS_OWNER_UID) & mask;
+
+                        if (m) {
+                                c->cgroup = strdup(item->str);
+                                if (!c->cgroup)
+                                        return -ENOMEM;
+
+                                c->mask |= m;
+                        }
+                        break;
+
+                case KDBUS_ITEM_CAPS:
+                        m = (SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_PERMITTED_CAPS |
+                             SD_BUS_CREDS_INHERITABLE_CAPS | SD_BUS_CREDS_BOUNDING_CAPS) & mask;
+
+                        if (m) {
+                                c->capability_size = item->size - KDBUS_PART_HEADER_SIZE;
+                                c->capability = memdup(item->data, c->capability_size);
+                                if (!c->capability)
+                                        return -ENOMEM;
+
+                                c->mask |= m;
+                        }
+                        break;
+
+                case KDBUS_ITEM_SECLABEL:
+                        if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
+                                c->label = strdup(item->str);
+                                if (!c->label)
+                                        return -ENOMEM;
+
+                                c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
+                        }
+                        break;
+
+                case KDBUS_ITEM_AUDIT:
+                        m = (SD_BUS_CREDS_AUDIT_SESSION_ID | SD_BUS_CREDS_AUDIT_LOGIN_UID) & mask;
+
+                        if (m) {
+                                c->audit_session_id = item->audit.sessionid;
+                                c->audit_login_uid = item->audit.loginuid;
+                                c->mask |= m;
+                        }
+                        break;
+                }
+        }
+
+        if (creds) {
+                *creds = c;
+                c = NULL;
+        }
+
+        if (owner) {
+                *owner = unique;
+                unique = NULL;
+        }
+
+        return 0;
+}
+
+_public_ int sd_bus_get_owner(
+                sd_bus *bus,
+                const char *name,
+                uint64_t mask,
+                char **owner,
+                sd_bus_creds **creds) {
+
+        assert_return(bus, -EINVAL);
+        assert_return(name, -EINVAL);
+        assert_return(mask <= _SD_BUS_CREDS_MAX, -ENOTSUP);
+        assert_return(mask == 0 || creds, -EINVAL);
+        assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
+
+        if (bus->is_kernel)
+                return kdbus_name_query(bus, name, mask, owner, creds);
+
+        return sd_bus_get_owner_dbus(bus, name, mask, owner, creds);
+}
+
 int bus_add_match_internal(
                 sd_bus *bus,
                 const char *match,
@@ -455,6 +811,8 @@ int bus_add_match_internal(
                 uint64_t src_id = KDBUS_MATCH_SRC_ID_ANY;
                 bool using_bloom = false;
                 unsigned i;
+                bool matches_name_change = true;
+                const char *name_change_arg[3] = {};
 
                 zero(bloom);
 
@@ -466,6 +824,9 @@ int bus_add_match_internal(
                         switch (c->type) {
 
                         case BUS_MATCH_SENDER:
+                                if (!streq(c->value_str, "org.freedesktop.DBus"))
+                                        matches_name_change = false;
+
                                 r = bus_kernel_parse_unique_name(c->value_str, &src_id);
                                 if (r < 0)
                                         return r;
@@ -479,21 +840,33 @@ int bus_add_match_internal(
                                 break;
 
                         case BUS_MATCH_MESSAGE_TYPE:
+                                if (c->value_u8 != SD_BUS_MESSAGE_SIGNAL)
+                                        matches_name_change = false;
+
                                 bloom_add_pair(bloom, "message-type", bus_message_type_to_string(c->value_u8));
                                 using_bloom = true;
                                 break;
 
                         case BUS_MATCH_INTERFACE:
+                                if (!streq(c->value_str, "org.freedesktop.DBus"))
+                                        matches_name_change = false;
+
                                 bloom_add_pair(bloom, "interface", c->value_str);
                                 using_bloom = true;
                                 break;
 
                         case BUS_MATCH_MEMBER:
+                                if (!streq(c->value_str, "NameOwnerChanged"))
+                                        matches_name_change = false;
+
                                 bloom_add_pair(bloom, "member", c->value_str);
                                 using_bloom = true;
                                 break;
 
                         case BUS_MATCH_PATH:
+                                if (!streq(c->value_str, "/org/freedesktop/DBus"))
+                                        matches_name_change = false;
+
                                 bloom_add_pair(bloom, "path", c->value_str);
                                 using_bloom = true;
                                 break;
@@ -508,6 +881,9 @@ int bus_add_match_internal(
                         case BUS_MATCH_ARG...BUS_MATCH_ARG_LAST: {
                                 char buf[sizeof("arg")-1 + 2 + 1];
 
+                                if (c->type - BUS_MATCH_ARG < 3)
+                                        name_change_arg[c->type - BUS_MATCH_ARG] = c->value_str;
+
                                 snprintf(buf, sizeof(buf), "arg%u", c->type - BUS_MATCH_ARG);
                                 bloom_add_pair(bloom, buf, c->value_str);
                                 using_bloom = true;
@@ -577,7 +953,20 @@ int bus_add_match_internal(
                 if (r < 0)
                         return -errno;
 
-        } else {
+                if (matches_name_change) {
+
+                        /* If this match could theoretically match
+                         * NameOwnerChanged messages, we need to
+                         * install a second non-bloom filter explitly
+                         * for it */
+
+                        r = add_name_change_match(bus, cookie, name_change_arg[0], name_change_arg[1], name_change_arg[2]);
+                        if (r < 0)
+                                return r;
+                }
+
+                return 0;
+        } else
                 return sd_bus_call_method(
                                 bus,
                                 "org.freedesktop.DBus",
@@ -588,9 +977,6 @@ int bus_add_match_internal(
                                 NULL,
                                 "s",
                                 match);
-        }
-
-        return 0;
 }
 
 int bus_remove_match_internal(
@@ -614,6 +1000,8 @@ int bus_remove_match_internal(
                 if (r < 0)
                         return -errno;
 
+                return 0;
+
         } else {
                 return sd_bus_call_method(
                                 bus,
@@ -626,8 +1014,6 @@ int bus_remove_match_internal(
                                 "s",
                                 match);
         }
-
-        return 0;
 }
 
 _public_ int sd_bus_get_owner_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {