permissions: pass pw_permission struct around

Use a pw_permission struct to express permissions of object.
Improve client permissions, add/remove globals when permissions
are changed.
This commit is contained in:
Wim Taymans 2018-11-05 15:02:08 +01:00
parent f994b7eb70
commit eb0a561f8c
13 changed files with 282 additions and 320 deletions

View file

@ -509,21 +509,18 @@ static void on_state_changed(void *_data, enum pw_remote_state old, enum pw_remo
case PW_REMOTE_STATE_CONNECTED: case PW_REMOTE_STATE_CONNECTED:
{ {
struct spa_dict_item items[5]; struct pw_permission permissions[2];
int i = 0;
/* set specific permissions on all existing objects without permissions */
items[i++] = SPA_DICT_ITEM_INIT(PW_CORE_PROXY_PERMISSIONS_EXISTING, "r--");
/* set default permission for new objects and objects without
* specific permissions */
items[i++] = SPA_DICT_ITEM_INIT(PW_CORE_PROXY_PERMISSIONS_DEFAULT, "---");
/* an example, set specific permissions on one object, this is the /* an example, set specific permissions on one object, this is the
* core object, we already have a binding to it that is not affected * core object. */
* by the removal of X permissions, only future bindings. */ permissions[0].id = 0;
items[i++] = SPA_DICT_ITEM_INIT(PW_CORE_PROXY_PERMISSIONS_GLOBAL, "0:rw-"); permissions[0].permissions = PW_PERM_R | PW_PERM_X;
/* remove WX from all other objects */
permissions[1].id = SPA_ID_INVALID;
permissions[1].permissions = PW_PERM_R;
pw_core_proxy_permissions(pw_remote_get_core_proxy(data->remote), pw_core_proxy_permissions(pw_remote_get_core_proxy(data->remote),
&SPA_DICT_INIT(items, i)); 2, permissions);
make_node(data); make_node(data);
break; break;

View file

@ -700,7 +700,7 @@ handle_client(struct impl *impl, uint32_t id, uint32_t parent_id,
{ {
struct pw_proxy *p; struct pw_proxy *p;
struct client *client; struct client *client;
struct spa_dict_item items[2]; struct pw_permission perms[2];
const char *str; const char *str;
p = pw_registry_proxy_bind(impl->registry_proxy, p = pw_registry_proxy_bind(impl->registry_proxy,
@ -727,10 +727,9 @@ handle_client(struct impl *impl, uint32_t id, uint32_t parent_id,
return 0; return 0;
if (strcmp(str, "restricted") == 0) { if (strcmp(str, "restricted") == 0) {
items[0].key = PW_CORE_PROXY_PERMISSIONS_DEFAULT; perms[0] = PW_PERMISSION_INIT(-1, PW_PERM_RWX);
items[0].value = "rwx";
pw_client_proxy_update_permissions((struct pw_client_proxy*)p, pw_client_proxy_update_permissions((struct pw_client_proxy*)p,
&SPA_DICT_INIT(items, 1)); 1, perms);
} }
return 0; return 0;
} }

View file

@ -110,6 +110,7 @@ core_check_access(void *data, struct pw_client *client)
{ {
struct impl *impl = data; struct impl *impl = data;
const struct ucred *ucred; const struct ucred *ucred;
struct pw_permission permissions[1];
struct spa_dict_item items[2]; struct spa_dict_item items[2];
const char *str; const char *str;
int res; int res;
@ -128,7 +129,8 @@ core_check_access(void *data, struct pw_client *client)
if (res == 0) if (res == 0)
goto granted; goto granted;
if (res > 0) if (res > 0)
res = EACCES; res = -EACCES;
items[0] = SPA_DICT_ITEM_INIT("pipewire.access", "blacklisted");
goto blacklisted; goto blacklisted;
} }
@ -162,7 +164,8 @@ core_check_access(void *data, struct pw_client *client)
granted: granted:
pw_log_debug("module %p: client %p access granted", impl, client); pw_log_debug("module %p: client %p access granted", impl, client);
pw_client_set_permissions(client, PW_PERM_RWX); permissions[0] = PW_PERMISSION_INIT(-1, PW_PERM_RWX);
pw_client_update_permissions(client, 1, permissions);
return; return;
wait_permissions: wait_permissions:
@ -172,7 +175,6 @@ core_check_access(void *data, struct pw_client *client)
return; return;
blacklisted: blacklisted:
items[0] = SPA_DICT_ITEM_INIT("pipewire.access", "blacklisted");
pw_resource_error(pw_client_get_core_resource(client), 0, res, "blacklisted"); pw_resource_error(pw_client_get_core_resource(client), 0, res, "blacklisted");
pw_client_update_properties(client, &SPA_DICT_INIT(items, 1)); pw_client_update_properties(client, &SPA_DICT_INIT(items, 1));
return; return;

View file

@ -64,22 +64,23 @@ static void core_marshal_client_update(void *object, const struct spa_dict *prop
pw_protocol_native_end_proxy(proxy, b); pw_protocol_native_end_proxy(proxy, b);
} }
static void core_marshal_permissions(void *object, const struct spa_dict *props) static void core_marshal_permissions(void *object, uint32_t n_permissions,
const struct pw_permission *permissions)
{ {
struct pw_proxy *proxy = object; struct pw_proxy *proxy = object;
struct spa_pod_builder *b; struct spa_pod_builder *b;
int i, n_items; int i;
b = pw_protocol_native_begin_proxy(proxy, PW_CORE_PROXY_METHOD_PERMISSIONS); b = pw_protocol_native_begin_proxy(proxy, PW_CORE_PROXY_METHOD_PERMISSIONS);
n_items = props ? props->n_items : 0; spa_pod_builder_add(b, "[",
"i", n_permissions,
NULL);
spa_pod_builder_add(b, "[ i", n_items, NULL); for (i = 0; i < n_permissions; i++) {
for (i = 0; i < n_items; i++) {
spa_pod_builder_add(b, spa_pod_builder_add(b,
"s", props->items[i].key, "i", permissions[i].id,
"s", props->items[i].value, NULL); "i", permissions[i].permissions, NULL);
} }
spa_pod_builder_add(b, "]", NULL); spa_pod_builder_add(b, "]", NULL);
@ -340,23 +341,25 @@ static int core_demarshal_client_update(void *object, void *data, size_t size)
static int core_demarshal_permissions(void *object, void *data, size_t size) static int core_demarshal_permissions(void *object, void *data, size_t size)
{ {
struct pw_resource *resource = object; struct pw_resource *resource = object;
struct spa_dict props;
struct spa_pod_parser prs; struct spa_pod_parser prs;
uint32_t i; struct pw_permission *permissions;
uint32_t i, n_permissions;
spa_pod_parser_init(&prs, data, size, 0); spa_pod_parser_init(&prs, data, size, 0);
if (spa_pod_parser_get(&prs, "[ i", &props.n_items, NULL) < 0) if (spa_pod_parser_get(&prs, "[",
"i", &n_permissions, NULL) < 0)
return -EINVAL; return -EINVAL;
props.items = alloca(props.n_items * sizeof(struct spa_dict_item)); permissions = alloca(n_permissions * sizeof(struct pw_permission));
for (i = 0; i < props.n_items; i++) { for (i = 0; i < n_permissions; i++) {
if (spa_pod_parser_get(&prs, if (spa_pod_parser_get(&prs,
"s", &props.items[i].key, "i", &permissions[i].id,
"s", &props.items[i].value, "i", &permissions[i].permissions,
NULL) < 0) NULL) < 0)
return -EINVAL; return -EINVAL;
} }
pw_resource_do(resource, struct pw_core_proxy_methods, permissions, 0, &props); pw_resource_do(resource, struct pw_core_proxy_methods, permissions, 0,
n_permissions, permissions);
return 0; return 0;
} }
@ -1017,24 +1020,23 @@ static int client_demarshal_info(void *object, void *data, size_t size)
return 0; return 0;
} }
static void client_marshal_permissions(void *object, const struct spa_dict *dict) static void client_marshal_permissions(void *object, uint32_t index, uint32_t n_permissions,
struct pw_permission *permissions)
{ {
struct pw_resource *resource = object; struct pw_resource *resource = object;
struct spa_pod_builder *b; struct spa_pod_builder *b;
uint32_t i, n_items; uint32_t i;
b = pw_protocol_native_begin_resource(resource, PW_CLIENT_PROXY_EVENT_PERMISSIONS); b = pw_protocol_native_begin_resource(resource, PW_CLIENT_PROXY_EVENT_PERMISSIONS);
n_items = dict ? dict->n_items : 0;
spa_pod_builder_add(b, spa_pod_builder_add(b,
"[", "[ i", index,
"i", n_items, NULL); "i", n_permissions, NULL);
for (i = 0; i < n_items; i++) { for (i = 0; i < n_permissions; i++) {
spa_pod_builder_add(b, spa_pod_builder_add(b,
"s", dict->items[i].key, "s", permissions[i].id,
"s", dict->items[i].value, NULL); "s", permissions[i].permissions, NULL);
} }
spa_pod_builder_add(b, "]", NULL); spa_pod_builder_add(b, "]", NULL);
@ -1044,23 +1046,24 @@ static void client_marshal_permissions(void *object, const struct spa_dict *dict
static int client_demarshal_permissions(void *object, void *data, size_t size) static int client_demarshal_permissions(void *object, void *data, size_t size)
{ {
struct pw_proxy *proxy = object; struct pw_proxy *proxy = object;
struct spa_dict props; struct pw_permission *permissions;
struct spa_pod_parser prs; struct spa_pod_parser prs;
uint32_t i; uint32_t i, index, n_permissions;
spa_pod_parser_init(&prs, data, size, 0); spa_pod_parser_init(&prs, data, size, 0);
if (spa_pod_parser_get(&prs, "[ i", &props.n_items, NULL) < 0) if (spa_pod_parser_get(&prs, "[ i", &index,
"i", &n_permissions, NULL) < 0)
return -EINVAL; return -EINVAL;
props.items = alloca(props.n_items * sizeof(struct spa_dict_item)); permissions = alloca(n_permissions * sizeof(struct pw_permission));
for (i = 0; i < props.n_items; i++) { for (i = 0; i < n_permissions; i++) {
if (spa_pod_parser_get(&prs, if (spa_pod_parser_get(&prs,
"s", &props.items[i].key, "i", &permissions[i].id,
"s", &props.items[i].value, "i", &permissions[i].permissions,
NULL) < 0) NULL) < 0)
return -EINVAL; return -EINVAL;
} }
pw_proxy_notify(proxy, struct pw_client_proxy_events, permissions, 0, &props); pw_proxy_notify(proxy, struct pw_client_proxy_events, permissions, 0, index, n_permissions, permissions);
return 0; return 0;
} }
@ -1095,14 +1098,16 @@ static int client_demarshal_error(void *object, void *data, size_t size)
return 0; return 0;
} }
static void client_marshal_get_permissions(void *object) static void client_marshal_get_permissions(void *object, uint32_t index, uint32_t num)
{ {
struct pw_proxy *proxy = object; struct pw_proxy *proxy = object;
struct spa_pod_builder *b; struct spa_pod_builder *b;
b = pw_protocol_native_begin_proxy(proxy, PW_CLIENT_PROXY_METHOD_GET_PERMISSIONS); b = pw_protocol_native_begin_proxy(proxy, PW_CLIENT_PROXY_METHOD_GET_PERMISSIONS);
spa_pod_builder_add_struct(b, "P", NULL); spa_pod_builder_add_struct(b,
"i", index,
"i", num, NULL);
pw_protocol_native_end_proxy(proxy, b); pw_protocol_native_end_proxy(proxy, b);
} }
@ -1111,32 +1116,33 @@ static int client_demarshal_get_permissions(void *object, void *data, size_t siz
{ {
struct pw_resource *resource = object; struct pw_resource *resource = object;
struct spa_pod_parser prs; struct spa_pod_parser prs;
void *ptr; uint32_t index, num;
spa_pod_parser_init(&prs, data, size, 0); spa_pod_parser_init(&prs, data, size, 0);
if (spa_pod_parser_get(&prs, "[P]", &ptr, NULL) < 0) if (spa_pod_parser_get(&prs,
"[i", &index,
"i", &num, NULL) < 0)
return -EINVAL; return -EINVAL;
pw_resource_do(resource, struct pw_client_proxy_methods, get_permissions, 0); pw_resource_do(resource, struct pw_client_proxy_methods, get_permissions, 0, index, num);
return 0; return 0;
} }
static void client_marshal_update_permissions(void *object, const struct spa_dict *props) static void client_marshal_update_permissions(void *object, uint32_t n_permissions,
const struct pw_permission *permissions)
{ {
struct pw_proxy *proxy = object; struct pw_proxy *proxy = object;
struct spa_pod_builder *b; struct spa_pod_builder *b;
int i, n_items; int i;
b = pw_protocol_native_begin_proxy(proxy, PW_CLIENT_PROXY_METHOD_UPDATE_PERMISSIONS); b = pw_protocol_native_begin_proxy(proxy, PW_CLIENT_PROXY_METHOD_UPDATE_PERMISSIONS);
n_items = props ? props->n_items : 0; spa_pod_builder_add(b, "[ i", n_permissions, NULL);
spa_pod_builder_add(b, "[ i", n_items, NULL); for (i = 0; i < n_permissions; i++) {
for (i = 0; i < n_items; i++) {
spa_pod_builder_add(b, spa_pod_builder_add(b,
"s", props->items[i].key, "i", permissions[i].id,
"s", props->items[i].value, NULL); "i", permissions[i].permissions, NULL);
} }
spa_pod_builder_add(b, "]", NULL); spa_pod_builder_add(b, "]", NULL);
@ -1146,23 +1152,24 @@ static void client_marshal_update_permissions(void *object, const struct spa_dic
static int client_demarshal_update_permissions(void *object, void *data, size_t size) static int client_demarshal_update_permissions(void *object, void *data, size_t size)
{ {
struct pw_resource *resource = object; struct pw_resource *resource = object;
struct spa_dict props; struct pw_permission *permissions;
struct spa_pod_parser prs; struct spa_pod_parser prs;
uint32_t i; uint32_t i, n_permissions;
spa_pod_parser_init(&prs, data, size, 0); spa_pod_parser_init(&prs, data, size, 0);
if (spa_pod_parser_get(&prs, "[ i", &props.n_items, NULL) < 0) if (spa_pod_parser_get(&prs, "[ i", &n_permissions, NULL) < 0)
return -EINVAL; return -EINVAL;
props.items = alloca(props.n_items * sizeof(struct spa_dict_item)); permissions = alloca(n_permissions * sizeof(struct pw_permission));
for (i = 0; i < props.n_items; i++) { for (i = 0; i < n_permissions; i++) {
if (spa_pod_parser_get(&prs, if (spa_pod_parser_get(&prs,
"s", &props.items[i].key, "i", &permissions[i].id,
"s", &props.items[i].value, "i", &permissions[i].permissions,
NULL) < 0) NULL) < 0)
return -EINVAL; return -EINVAL;
} }
pw_resource_do(resource, struct pw_client_proxy_methods, update_permissions, 0, &props); pw_resource_do(resource, struct pw_client_proxy_methods, update_permissions, 0,
n_permissions, permissions);
return 0; return 0;
} }

View file

@ -27,11 +27,6 @@
#include "pipewire/private.h" #include "pipewire/private.h"
#include "pipewire/resource.h" #include "pipewire/resource.h"
struct permission {
uint32_t id;
uint32_t permissions;
};
/** \cond */ /** \cond */
struct impl { struct impl {
struct pw_client this; struct pw_client this;
@ -46,40 +41,40 @@ struct resource_data {
}; };
/** find a specific permission for a global or NULL when there is none */ /** find a specific permission for a global or NULL when there is none */
static struct permission * static struct pw_permission *
find_permission(struct pw_client *client, uint32_t id) find_permission(struct pw_client *client, uint32_t id)
{ {
struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this); struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
struct permission *p; struct pw_permission *p;
if (!pw_array_check_index(&impl->permissions, id, struct permission)) if (!pw_array_check_index(&impl->permissions, id, struct pw_permission))
return NULL; return NULL;
p = pw_array_get_unchecked(&impl->permissions, id, struct permission); p = pw_array_get_unchecked(&impl->permissions, id, struct pw_permission);
if (p->permissions == -1) if (p->permissions == -1)
return NULL; return NULL;
else else
return p; return p;
} }
static struct permission *ensure_permissions(struct pw_client *client, uint32_t id) static struct pw_permission *ensure_permissions(struct pw_client *client, uint32_t id)
{ {
struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this); struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
struct permission *p; struct pw_permission *p;
size_t len, i; size_t len, i;
len = pw_array_get_len(&impl->permissions, struct permission); len = pw_array_get_len(&impl->permissions, struct pw_permission);
if (len <= id) { if (len <= id) {
size_t diff = id - len + 1; size_t diff = id - len + 1;
p = pw_array_add(&impl->permissions, diff * sizeof(struct permission)); p = pw_array_add(&impl->permissions, diff * sizeof(struct pw_permission));
if (p == NULL) if (p == NULL)
return NULL; return NULL;
for (i = 0; i < diff; i++) for (i = 0; i < diff; i++)
p[i].permissions = -1; p[i].permissions = -1;
} }
p = pw_array_get_unchecked(&impl->permissions, id, struct permission); p = pw_array_get_unchecked(&impl->permissions, id, struct pw_permission);
return p; return p;
} }
@ -90,7 +85,7 @@ client_permission_func(struct pw_global *global,
struct pw_client *client, void *data) struct pw_client *client, void *data)
{ {
struct impl *impl = data; struct impl *impl = data;
struct permission *p; struct pw_permission *p;
p = find_permission(client, global->id); p = find_permission(client, global->id);
if (p == NULL) if (p == NULL)
@ -99,26 +94,6 @@ client_permission_func(struct pw_global *global,
return p->permissions; return p->permissions;
} }
static uint32_t parse_mask(const char *str)
{
uint32_t mask = 0;
while (*str != '\0') {
switch (*str++) {
case 'r':
mask |= PW_PERM_R;
break;
case 'w':
mask |= PW_PERM_W;
break;
case 'x':
mask |= PW_PERM_X;
break;
}
}
return mask;
}
static void client_error(void *object, uint32_t id, int res, const char *error) static void client_error(void *object, uint32_t id, int res, const char *error)
{ {
struct pw_resource *resource = object; struct pw_resource *resource = object;
@ -127,65 +102,21 @@ static void client_error(void *object, uint32_t id, int res, const char *error)
pw_resource_error(client->core_resource, id, res, error); pw_resource_error(client->core_resource, id, res, error);
} }
static void client_get_permissions(void *object) static void client_get_permissions(void *object, uint32_t index, uint32_t num)
{ {
struct pw_resource *resource = object; struct pw_resource *resource = object;
struct resource_data *data = pw_resource_get_user_data(resource); struct resource_data *data = pw_resource_get_user_data(resource);
struct pw_client *client = data->client; struct pw_client *client = data->client;
pw_log_debug("client %p", client);
} }
static void client_update_permissions(void *object, const struct spa_dict *props) static void client_update_permissions(void *object,
uint32_t n_permissions, const struct pw_permission *permissions)
{ {
struct pw_resource *resource = object; struct pw_resource *resource = object;
struct resource_data *data = pw_resource_get_user_data(resource); struct resource_data *data = pw_resource_get_user_data(resource);
struct pw_client *client = data->client; struct pw_client *client = data->client;
struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this); pw_client_update_permissions(client, n_permissions, permissions);
const char *str;
int i, len;
for (i = 0; i < props->n_items; i++) {
str = props->items[i].value;
pw_log_debug("client %p: %s %s", client, props->items[i].key, str);
if (strcmp(props->items[i].key, PW_CORE_PROXY_PERMISSIONS_DEFAULT) == 0) {
impl->permissions_default = parse_mask(str);
pw_log_debug("client %p: set default permissions to %08x",
client, impl->permissions_default);
}
else if (strcmp(props->items[i].key, PW_CORE_PROXY_PERMISSIONS_GLOBAL) == 0) {
struct pw_global *global;
uint32_t global_id, old_perm, new_perm;
struct permission *p;
/* permissions.update=<global-id>:[r][w][x] */
len = strcspn(str, ":");
if (len == 0)
continue;
global_id = atoi(str);
global = pw_core_find_global(client->core, global_id);
if (global == NULL) {
pw_log_warn("client %p: invalid global %d", client, global_id);
continue;
}
p = ensure_permissions(client, global_id);
old_perm = p->permissions == -1 ? impl->permissions_default : p->permissions;
new_perm = parse_mask(str + len);
pw_log_debug("client %p: %08x %08x", client, old_perm, new_perm);
p->permissions = new_perm;
if (PW_PERM_IS_R(old_perm) && !PW_PERM_IS_R(new_perm)) {
pw_global_revoke(global, client);
}
else if (!PW_PERM_IS_R(old_perm) && PW_PERM_IS_R(new_perm)) {
pw_global_grant(global, client);
}
}
}
pw_client_set_busy(client, false); pw_client_set_busy(client, false);
} }
@ -246,7 +177,7 @@ core_global_removed(void *data, struct pw_global *global)
{ {
struct impl *impl = data; struct impl *impl = data;
struct pw_client *client = &impl->this; struct pw_client *client = &impl->this;
struct permission *p; struct pw_permission *p;
p = find_permission(client, global->id); p = find_permission(client, global->id);
pw_log_debug("client %p: global %d removed, %p", client, global->id, p); pw_log_debug("client %p: global %d removed, %p", client, global->id, p);
@ -492,91 +423,61 @@ int pw_client_update_properties(struct pw_client *client, const struct spa_dict
return changed; return changed;
} }
struct permissions_update { int pw_client_update_permissions(struct pw_client *client,
struct pw_client *client; uint32_t n_permissions, const struct pw_permission *permissions)
uint32_t permissions;
bool only_new;
};
static int do_permissions(void *data, struct pw_global *global)
{
struct permissions_update *update = data;
struct pw_client *client = update->client;
struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
struct permission *p;
p = ensure_permissions(client, global->id);
if (p == NULL)
return -ENOMEM;
if (p->permissions == -1)
p->permissions = impl->permissions_default;
else if (update->only_new)
return 0;
p->permissions &= update->permissions;
pw_log_debug("client %p: set global %d permissions to %08x", client, global->id, p->permissions);
return 0;
}
int pw_client_update_permissions(struct pw_client *client, const struct spa_dict *dict)
{ {
struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this); struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
struct pw_core *core = client->core;
int i; int i;
const char *str;
size_t len;
struct permissions_update update = { client, 0 };
uint32_t permissions_existing, permissions_default;
permissions_default = impl->permissions_default; for (i = 0; i < n_permissions; i++) {
permissions_existing = -1; struct pw_permission *p;
uint32_t old_perm, new_perm;
for (i = 0; i < dict->n_items; i++) {
str = dict->items[i].value;
if (strcmp(dict->items[i].key, PW_CORE_PROXY_PERMISSIONS_DEFAULT) == 0) {
permissions_default &= parse_mask(str);
pw_log_debug("client %p: set default permissions to %08x",
client, permissions_default);
}
else if (strcmp(dict->items[i].key, PW_CORE_PROXY_PERMISSIONS_GLOBAL) == 0) {
struct pw_global *global; struct pw_global *global;
uint32_t global_id;
/* permissions.update=<global-id>:[r][w][x] */ if (permissions[i].id == SPA_ID_INVALID) {
len = strcspn(str, ":"); old_perm = impl->permissions_default;
if (len == 0) new_perm = permissions[i].permissions;
if (core->current_client == client)
new_perm &= old_perm;
pw_log_debug("client %p: set default permissions %08x -> %08x",
client, old_perm, new_perm);
spa_list_for_each(global, &core->global_list, link) {
p = find_permission(client, global->id);
if (p != NULL)
continue; continue;
global_id = atoi(str); pw_global_update_permissions(global, client, old_perm, new_perm);
global = pw_core_find_global(client->core, global_id); }
impl->permissions_default = new_perm;
}
else {
struct pw_global *global;
global = pw_core_find_global(client->core, permissions[i].id);
if (global == NULL) { if (global == NULL) {
pw_log_warn("client %p: invalid global %d", client, global_id); pw_log_warn("client %p: invalid global %d", client, permissions[i].id);
continue; continue;
} }
p = ensure_permissions(client, global->id);
old_perm = p->permissions == -1 ? impl->permissions_default : p->permissions;
new_perm = permissions[i].permissions;
/* apply the specific updates in order. This is ok for now, we could add if (core->current_client == client)
* a field to the permission struct later to accumulate the changes new_perm &= old_perm;
* and apply them out of this loop */
update.permissions = parse_mask(str + len);
update.only_new = false;
do_permissions(&update, global);
}
else if (strcmp(dict->items[i].key, PW_CORE_PROXY_PERMISSIONS_EXISTING) == 0) {
permissions_existing = parse_mask(str);
pw_log_debug("client %p: set existing permissions to %08x",
client, permissions_existing);
}
}
/* apply default and existing permissions after specific ones to make the
* permission update look like an atomic unordered set of changes. */
if (permissions_existing != -1) {
update.permissions = permissions_existing;
update.only_new = true;
pw_core_for_each_global(client->core, do_permissions, &update);
}
impl->permissions_default = permissions_default;
pw_log_debug("client %p: set global %d permissions %08x -> %08x",
client, global->id, old_perm, new_perm);
pw_global_update_permissions(global, client, old_perm, new_perm);
p->permissions = new_perm;
}
}
if (n_permissions > 0)
pw_client_set_busy(client, false);
return 0; return 0;
} }
@ -588,11 +489,3 @@ void pw_client_set_busy(struct pw_client *client, bool busy)
pw_client_events_busy_changed(client, busy); pw_client_events_busy_changed(client, busy);
} }
} }
void pw_client_set_permissions(struct pw_client *client, uint32_t permissions)
{
struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
pw_log_debug("client %p: permissions %08x", client, permissions);
impl->permissions_default = permissions;
pw_client_set_busy(client, false);
}

View file

@ -49,6 +49,7 @@ struct pw_client;
#include <pipewire/introspect.h> #include <pipewire/introspect.h>
#include <pipewire/properties.h> #include <pipewire/properties.h>
#include <pipewire/resource.h> #include <pipewire/resource.h>
#include <pipewire/permission.h>
#define PW_TYPE__Client PW_TYPE_OBJECT_BASE "Client" #define PW_TYPE__Client PW_TYPE_OBJECT_BASE "Client"
#define PW_TYPE_CLIENT_BASE PW_TYPE__Client ":" #define PW_TYPE_CLIENT_BASE PW_TYPE__Client ":"
@ -146,7 +147,8 @@ const struct pw_client_info *pw_client_get_info(struct pw_client *client);
int pw_client_update_properties(struct pw_client *client, const struct spa_dict *dict); int pw_client_update_properties(struct pw_client *client, const struct spa_dict *dict);
/** Update the client permissions */ /** Update the client permissions */
int pw_client_update_permissions(struct pw_client *client, const struct spa_dict *dict); int pw_client_update_permissions(struct pw_client *client, uint32_t n_permissions,
const struct pw_permission *permissions);
/** Get the client properties */ /** Get the client properties */
const struct pw_properties *pw_client_get_properties(struct pw_client *client); const struct pw_properties *pw_client_get_properties(struct pw_client *client);
@ -177,8 +179,6 @@ void pw_client_add_listener(struct pw_client *client,
* started and no further processing is allowed to happen for the client */ * started and no further processing is allowed to happen for the client */
void pw_client_set_busy(struct pw_client *client, bool busy); void pw_client_set_busy(struct pw_client *client, bool busy);
void pw_client_set_permissions(struct pw_client *client, uint32_t permissions);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -64,11 +64,11 @@ static void registry_bind(void *object, uint32_t id,
if (!PW_PERM_IS_R(permissions)) if (!PW_PERM_IS_R(permissions))
goto no_id; goto no_id;
if (type != global->type) if (global->type != type)
goto wrong_interface; goto wrong_interface;
pw_log_debug("global %p: bind global id %d, iface %s to %d", global, id, pw_log_debug("global %p: bind global id %d, iface %s/%d to %d", global, id,
spa_debug_type_find_name(pw_type_info(), type), new_id); spa_debug_type_find_name(pw_type_info(), type), version, new_id);
if (pw_global_bind(global, client, permissions, version, new_id) < 0) if (pw_global_bind(global, client, permissions, version, new_id) < 0)
goto exit; goto exit;
@ -77,9 +77,13 @@ static void registry_bind(void *object, uint32_t id,
no_id: no_id:
pw_log_debug("registry %p: no global with id %u to bind to %u", resource, id, new_id); pw_log_debug("registry %p: no global with id %u to bind to %u", resource, id, new_id);
pw_core_resource_error(client->core_resource, id,
-ENOENT, "no such global %u", id);
goto exit; goto exit;
wrong_interface: wrong_interface:
pw_log_debug("registry %p: global with id %u has no interface %u", resource, id, type); pw_log_debug("registry %p: global with id %u has no interface %u", resource, id, type);
pw_core_resource_error(client->core_resource, id,
-ENOENT, "no such interface %u", type);
goto exit; goto exit;
exit: exit:
/* unmark the new_id the map, the client does not yet know about the failed /* unmark the new_id the map, the client does not yet know about the failed
@ -151,10 +155,11 @@ static void core_client_update(void *object, const struct spa_dict *props)
pw_client_update_properties(resource->client, props); pw_client_update_properties(resource->client, props);
} }
static void core_permissions(void *object, const struct spa_dict *props) static void core_permissions(void *object, uint32_t n_permissions,
const struct pw_permission *permissions)
{ {
struct pw_resource *resource = object; struct pw_resource *resource = object;
pw_client_update_permissions(resource->client, props); pw_client_update_permissions(resource->client, n_permissions, permissions);
} }
static void core_sync(void *object, uint32_t seq) static void core_sync(void *object, uint32_t seq)

View file

@ -73,16 +73,6 @@ struct pw_core;
* resources of a PipeWire instance. * resources of a PipeWire instance.
*/ */
#define PW_PERM_R 0400 /**< object can be seen and events can be received */
#define PW_PERM_W 0200 /**< methods can be called that modify the object */
#define PW_PERM_X 0100 /**< methods can be called on the object. The W flag must be
* present in order to call methods that modify the object. */
#define PW_PERM_RWX (PW_PERM_R|PW_PERM_W|PW_PERM_X)
#define PW_PERM_IS_R(p) (((p)&PW_PERM_R) == PW_PERM_R)
#define PW_PERM_IS_W(p) (((p)&PW_PERM_W) == PW_PERM_W)
#define PW_PERM_IS_X(p) (((p)&PW_PERM_X) == PW_PERM_X)
/** core events emited by the core object added with \ref pw_core_add_listener */ /** core events emited by the core object added with \ref pw_core_add_listener */
struct pw_core_events { struct pw_core_events {
#define PW_VERSION_CORE_EVENTS 0 #define PW_VERSION_CORE_EVENTS 0

View file

@ -147,17 +147,19 @@ static int global_unregister(struct pw_global *global)
{ {
struct impl *impl = SPA_CONTAINER_OF(global, struct impl, this); struct impl *impl = SPA_CONTAINER_OF(global, struct impl, this);
struct pw_core *core = global->core; struct pw_core *core = global->core;
struct pw_resource *registry; struct pw_resource *resource;
if (!impl->registered) if (!impl->registered)
return 0; return 0;
spa_list_for_each(registry, &core->registry_resource_list, link) { spa_list_for_each(resource, &core->registry_resource_list, link) {
uint32_t permissions = pw_global_get_permissions(global, registry->client); uint32_t permissions = pw_global_get_permissions(global, resource->client);
pw_log_debug("registry %p: global %d %08x", registry, global->id, permissions); pw_log_debug("registry %p: global %d %08x", resource, global->id, permissions);
if (PW_PERM_IS_R(permissions)) if (PW_PERM_IS_R(permissions))
pw_registry_resource_global_remove(registry, global->id); pw_registry_resource_global_remove(resource, global->id);
} }
spa_list_consume(resource, &global->resource_list, link)
pw_resource_destroy(resource);
spa_list_remove(&global->link); spa_list_remove(&global->link);
pw_core_events_global_removed(core, global); pw_core_events_global_removed(core, global);
@ -220,11 +222,11 @@ void pw_global_add_listener(struct pw_global *global,
* \param global the global to bind to * \param global the global to bind to
* \param client the client that binds * \param client the client that binds
* \param version the version * \param version the version
* \param id the id * \param id the id of the resource
* *
* Let \a client bind to \a global with the given version and id. * Let \a client bind to \a global with the given version and id.
* After binding, the client and the global object will be able to * After binding, the client and the global object will be able to
* exchange messages. * exchange messages on the proxy/resource with \a id.
* *
* \memberof pw_global * \memberof pw_global
*/ */
@ -249,47 +251,39 @@ pw_global_bind(struct pw_global *global, struct pw_client *client, uint32_t perm
return res; return res;
} }
int pw_global_grant(struct pw_global *global, struct pw_client *client) int pw_global_update_permissions(struct pw_global *global, struct pw_client *client,
uint32_t old_permissions, uint32_t new_permissions)
{ {
struct pw_resource *registry;
struct pw_core *core = global->core; struct pw_core *core = global->core;
struct pw_resource *resource, *t;
spa_list_for_each(registry, &core->registry_resource_list, link) { spa_list_for_each(resource, &core->registry_resource_list, link) {
uint32_t permissions; if (resource->client != client)
if (registry->client != client)
continue; continue;
permissions = pw_global_get_permissions(global, client); if (PW_PERM_IS_R(old_permissions) && !PW_PERM_IS_R(new_permissions)) {
pw_registry_resource_global_remove(resource, global->id);
pw_log_debug("registry %p: global %d %08x", registry, global->id, permissions); }
if (PW_PERM_IS_R(permissions)) else if (!PW_PERM_IS_R(old_permissions) && PW_PERM_IS_R(new_permissions)) {
pw_registry_resource_global(registry, pw_registry_resource_global(resource,
global->id, global->id,
global->parent->id, global->parent->id,
permissions, new_permissions,
global->type, global->type,
global->version, global->version,
global->properties ? global->properties ?
&global->properties->dict : NULL); &global->properties->dict : NULL);
} }
return 0;
}
int pw_global_revoke(struct pw_global *global, struct pw_client *client)
{
struct pw_resource *registry, *resource, *t;
struct pw_core *core = global->core;
spa_list_for_each(registry, &core->registry_resource_list, link) {
if (registry->client != client)
continue;
pw_registry_resource_global_remove(registry, global->id);
} }
spa_list_for_each_safe(resource, t, &global->resource_list, link) { spa_list_for_each_safe(resource, t, &global->resource_list, link) {
if (resource->client != client) if (resource->client != client)
continue; continue;
/* don't ever destroy the core resource */
if (!PW_PERM_IS_R(new_permissions) && global->id != 0)
pw_resource_destroy(resource); pw_resource_destroy(resource);
else
resource->permissions = new_permissions;
} }
return 0; return 0;
} }

View file

@ -128,13 +128,8 @@ int pw_global_bind(struct pw_global *global,
uint32_t version, uint32_t version,
uint32_t id); uint32_t id);
/** Revoke access to global for client, the global will be removed from int pw_global_update_permissions(struct pw_global *global, struct pw_client *client,
* the client registry and all the bound resources for the client will be uint32_t old_permissions, uint32_t new_permissions);
* destroyed */
int pw_global_revoke(struct pw_global *global, struct pw_client *client);
/** Grant access to a global for client. The client registry will be
* notified of a new global */
int pw_global_grant(struct pw_global *global, struct pw_client *client);
/** Destroy a global */ /** Destroy a global */
void pw_global_destroy(struct pw_global *global); void pw_global_destroy(struct pw_global *global);

View file

@ -30,6 +30,7 @@ extern "C" {
#include <pipewire/introspect.h> #include <pipewire/introspect.h>
#include <pipewire/proxy.h> #include <pipewire/proxy.h>
#include <pipewire/permission.h>
struct pw_core_proxy; struct pw_core_proxy;
struct pw_registry_proxy; struct pw_registry_proxy;
@ -143,18 +144,21 @@ struct pw_core_proxy_methods {
*/ */
void (*client_update) (void *object, const struct spa_dict *props); void (*client_update) (void *object, const struct spa_dict *props);
/** /**
* Manage the permissions of the global objects * Manage the permissions of the global objects for this
* client
* *
* Update the permissions of the global objects using the * Update the permissions of the global objects using the
* dictionary with properties. * provided array with permissions
* *
* Globals can use the default permissions or can have specific * Globals can use the default permissions or can have specific
* permissions assigned to them. * permissions assigned to them.
* *
* \param id the global id to change * \param n_permissions number of permissions
* \param props dictionary with permission properties * \param permissions array of permissions
*/ */
void (*permissions) (void *object, const struct spa_dict *props); void (*permissions) (void *object,
uint32_t n_permissions,
const struct pw_permission *permissions);
/** /**
* Create a new object on the PipeWire server from a factory. * Create a new object on the PipeWire server from a factory.
* Use a \a factory_name of "client-node" to create a * Use a \a factory_name of "client-node" to create a
@ -209,9 +213,9 @@ pw_core_proxy_client_update(struct pw_core_proxy *core, const struct spa_dict *p
} }
static inline void static inline void
pw_core_proxy_permissions(struct pw_core_proxy *core, const struct spa_dict *props) pw_core_proxy_permissions(struct pw_core_proxy *core, uint32_t n_permissions, struct pw_permission *permissions)
{ {
pw_proxy_do((struct pw_proxy*)core, struct pw_core_proxy_methods, permissions, props); pw_proxy_do((struct pw_proxy*)core, struct pw_core_proxy_methods, permissions, n_permissions, permissions);
} }
static inline void * static inline void *
@ -696,9 +700,15 @@ struct pw_client_proxy_events {
* *
* Event emited as a result of the get_permissions method. * Event emited as a result of the get_permissions method.
* *
* \param param the parameter * \param default_permissions the default permissions
* \param index the index of the first permission entry
* \param n_permissions the number of permissions
* \param permissions the permissions
*/ */
void (*permissions) (void *object, const struct spa_dict *dict); void (*permissions) (void *object,
uint32_t index,
uint32_t n_permissions,
struct pw_permission *permissions);
}; };
/** Client */ /** Client */
@ -736,15 +746,20 @@ struct pw_client_proxy_methods {
* Get client permissions * Get client permissions
* *
* A permissions event will be emited with the permissions. * A permissions event will be emited with the permissions.
*
* \param index the first index to query, 0 for first
* \param num the maximum number of items to get
*/ */
void (*get_permissions) (void *object); void (*get_permissions) (void *object, uint32_t index, uint32_t num);
/** /**
* Update client permissions * Update client permissions
* *
* \param dict list of permissions to update * \param n_permissions number of permissions
* \param permissions array of new permissions
*/ */
void (*update_permissions) (void *object, const struct spa_dict *dict); void (*update_permissions) (void *object, uint32_t n_permissions,
const struct pw_permission *permissions);
}; };
/** Client permissions */ /** Client permissions */
@ -755,15 +770,17 @@ pw_client_proxy_error(struct pw_client_proxy *client, uint32_t id, int res, cons
} }
static inline void static inline void
pw_client_proxy_get_permissions(struct pw_client_proxy *client) pw_client_proxy_get_permissions(struct pw_client_proxy *client, uint32_t index, uint32_t num)
{ {
pw_proxy_do((struct pw_proxy*)client, struct pw_client_proxy_methods, get_permissions); pw_proxy_do((struct pw_proxy*)client, struct pw_client_proxy_methods, get_permissions, index, num);
} }
static inline void static inline void
pw_client_proxy_update_permissions(struct pw_client_proxy *client, const struct spa_dict *dict) pw_client_proxy_update_permissions(struct pw_client_proxy *client, uint32_t n_permissions,
const struct pw_permission *permissions)
{ {
pw_proxy_do((struct pw_proxy*)client, struct pw_client_proxy_methods, update_permissions, dict); pw_proxy_do((struct pw_proxy*)client, struct pw_client_proxy_methods, update_permissions,
n_permissions, permissions);
} }
#define PW_VERSION_LINK 0 #define PW_VERSION_LINK 0

60
src/pipewire/permission.h Normal file
View file

@ -0,0 +1,60 @@
/* PipeWire
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __PIPEWIRE_PERMISSION_H__
#define __PIPEWIRE_PERMISSION_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <spa/utils/defs.h>
/** \class pw_permission
*
* \brief a PipeWire permission
*
* Permissions are kept for a client and describe what the client is
* allowed to do with an object.
*
* See \ref page_core_api
*/
#define PW_PERM_R 0400 /**< object can be seen and events can be received */
#define PW_PERM_W 0200 /**< methods can be called that modify the object */
#define PW_PERM_X 0100 /**< methods can be called on the object. The W flag must be
* present in order to call methods that modify the object. */
#define PW_PERM_RWX (PW_PERM_R|PW_PERM_W|PW_PERM_X)
#define PW_PERM_IS_R(p) (((p)&PW_PERM_R) == PW_PERM_R)
#define PW_PERM_IS_W(p) (((p)&PW_PERM_W) == PW_PERM_W)
#define PW_PERM_IS_X(p) (((p)&PW_PERM_X) == PW_PERM_X)
struct pw_permission {
uint32_t id; /**< id of object, SPA_ID_INVALID for default permission */
uint32_t permissions; /**< bitmask of above permissions */
};
#define PW_PERMISSION_INIT(id,p) (struct pw_permission){ (id), (p) }
#ifdef __cplusplus
}
#endif
#endif /* __PIPEWIRE_PERMISSION_H__ */

View file

@ -32,6 +32,7 @@
#include <pipewire/command.h> #include <pipewire/command.h>
#include <pipewire/interfaces.h> #include <pipewire/interfaces.h>
#include <pipewire/type.h> #include <pipewire/type.h>
#include <pipewire/permission.h>
static const char WHITESPACE[] = " \t"; static const char WHITESPACE[] = " \t";
@ -1164,15 +1165,15 @@ static bool do_port_params(struct data *data, const char *cmd, char *args, char
static bool do_permissions(struct data *data, const char *cmd, char *args, char **error) static bool do_permissions(struct data *data, const char *cmd, char *args, char **error)
{ {
struct remote_data *rd = data->current; struct remote_data *rd = data->current;
char *a[2]; char *a[3];
int n; int n;
uint32_t id; uint32_t id;
struct global *global; struct global *global;
struct spa_dict_item items[1]; struct pw_permission permissions[1];
n = pw_split_ip(args, WHITESPACE, 2, a); n = pw_split_ip(args, WHITESPACE, 3, a);
if (n < 2) { if (n < 3) {
asprintf(error, "%s <client-id> <permission>", cmd); asprintf(error, "%s <client-id> <object> <permission>", cmd);
return false; return false;
} }
@ -1191,9 +1192,11 @@ static bool do_permissions(struct data *data, const char *cmd, char *args, char
return false; return false;
} }
items[0] = SPA_DICT_ITEM_INIT(PW_CORE_PROXY_PERMISSIONS_GLOBAL, a[1]);
permissions[0] = PW_PERMISSION_INIT(atoi(a[1]), atoi(a[2]));
pw_client_proxy_update_permissions((struct pw_client_proxy*)global->proxy, pw_client_proxy_update_permissions((struct pw_client_proxy*)global->proxy,
&SPA_DICT_INIT(items, 1)); 1, permissions);
return true; return true;
} }