+ if (creds) {
+ *creds = c;
+ c = NULL;
+ }
+
+ r = 0;
+
+fail:
+ ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd->offset);
+ return r;
+}
+
+static int bus_get_owner_dbus1(
+ sd_bus *bus,
+ const char *name,
+ uint64_t mask,
+ sd_bus_creds **creds) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *reply_unique = NULL, *reply = NULL;
+ _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
+ const char *unique = NULL;
+ pid_t pid = 0;
+ int r;
+
+ /* Only query the owner if the caller wants to know it or if
+ * the caller just wants to check whether a name exists */
+ if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) {
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetNameOwner",
+ NULL,
+ &reply_unique,
+ "s",
+ name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply_unique, "s", &unique);
+ if (r < 0)
+ return r;
+ }
+
+ if (mask != 0) {
+ c = bus_creds_new();
+ if (!c)
+ return -ENOMEM;
+
+ if ((mask & SD_BUS_CREDS_UNIQUE_NAME) && unique) {
+ c->unique_name = strdup(unique);
+ if (!c->unique_name)
+ return -ENOMEM;
+
+ c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
+ }
+
+ if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_GID|
+ SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
+ 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|
+ SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
+ SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)) {
+ uint32_t u;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetConnectionUnixProcessID",
+ NULL,
+ &reply,
+ "s",
+ unique ? unique : name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "u", &u);
+ if (r < 0)
+ return r;
+
+ pid = u;
+ if (mask & SD_BUS_CREDS_PID) {
+ c->pid = u;
+ c->mask |= SD_BUS_CREDS_PID;
+ }
+
+ reply = sd_bus_message_unref(reply);
+ }
+
+ if (mask & SD_BUS_CREDS_UID) {
+ uint32_t u;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetConnectionUnixUser",
+ NULL,
+ &reply,
+ "s",
+ unique ? unique : name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "u", &u);
+ if (r < 0)
+ return r;
+
+ c->uid = u;
+ c->mask |= SD_BUS_CREDS_UID;
+
+ reply = sd_bus_message_unref(reply);
+ }
+
+ if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
+ const void *p;
+ size_t sz;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetConnectionSELinuxSecurityContext",
+ NULL,
+ &reply,
+ "s",
+ unique ? unique : name);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_array(reply, 'y', &p, &sz);
+ if (r < 0)
+ return r;
+
+ c->label = strndup(p, sz);
+ if (!c->label)
+ return -ENOMEM;
+
+ c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
+ }
+
+ r = bus_creds_add_more(c, mask, pid, 0);
+ if (r < 0)
+ return r;
+ }
+
+ if (creds) {
+ *creds = c;
+ c = NULL;
+ }
+
+ return 0;
+}
+
+_public_ int sd_bus_get_owner(
+ sd_bus *bus,
+ const char *name,
+ uint64_t mask,
+ sd_bus_creds **creds) {
+
+ assert_return(bus, -EINVAL);
+ assert_return(name, -EINVAL);
+ assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP);
+ assert_return(mask == 0 || creds, -EINVAL);
+ assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+ assert_return(service_name_is_valid(name), -EINVAL);
+ assert_return(bus->bus_client, -ENODATA);
+
+ if (bus->is_kernel)
+ return bus_get_owner_kdbus(bus, name, mask, creds);
+ else
+ return bus_get_owner_dbus1(bus, name, mask, creds);
+}
+
+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 = KDBUS_MATCH_ID_ANY, 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_ITEM_NAME_{ADD,REMOVE,CHANGE} and
+ * KDBUS_ITEM_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_ITEM_NAME_ADD
+ * WELLKNOWN, UNIQUE, "" → KDBUS_ITEM_NAME_REMOVE
+ * WELLKNOWN, UNIQUE, UNIQUE → KDBUS_ITEM_NAME_CHANGE
+ * UNIQUE, "", UNIQUE → KDBUS_ITEM_ID_ADD
+ * UNIQUE, UNIQUE, "" → KDBUS_ITEM_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 (!isempty(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;
+ } else
+ old_owner_id = KDBUS_MATCH_ID_ANY;
+
+ if (!isempty(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;
+ } else
+ new_owner_id = KDBUS_MATCH_ID_ANY;
+
+ if (is_name_id <= 0) {
+ struct kdbus_cmd_match *m;
+ size_t sz, l;
+
+ /* If the name argument is missing or is a well-known
+ * name, then add KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}
+ * matches for it */
+
+ l = name ? strlen(name) + 1 : 0;
+
+ sz = ALIGN8(offsetof(struct kdbus_cmd_match, items) +
+ offsetof(struct kdbus_item, name_change) +
+ offsetof(struct kdbus_notify_name_change, name) +
+ l);