mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
Drop the current client context to check permissions. This restores the previous behaviour and fixes the permissions set by the portal module. This fixes screen sharing again. Fixes #362
713 lines
19 KiB
C
713 lines
19 KiB
C
/* PipeWire
|
|
*
|
|
* Copyright © 2018 Wim Taymans
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#include "pipewire/impl.h"
|
|
#include "pipewire/private.h"
|
|
|
|
#define NAME "client"
|
|
|
|
/** \cond */
|
|
struct impl {
|
|
struct pw_impl_client this;
|
|
struct spa_hook context_listener;
|
|
struct pw_array permissions;
|
|
struct spa_hook pool_listener;
|
|
unsigned int registered:1;
|
|
};
|
|
|
|
#define pw_client_resource(r,m,v,...) pw_resource_call(r,struct pw_client_events,m,v,__VA_ARGS__)
|
|
#define pw_client_resource_info(r,...) pw_client_resource(r,info,0,__VA_ARGS__)
|
|
#define pw_client_resource_permissions(r,...) pw_client_resource(r,permissions,0,__VA_ARGS__)
|
|
|
|
struct resource_data {
|
|
struct spa_hook resource_listener;
|
|
struct spa_hook object_listener;
|
|
struct pw_impl_client *client;
|
|
};
|
|
|
|
/** find a specific permission for a global or the default when there is none */
|
|
static struct pw_permission *
|
|
find_permission(struct pw_impl_client *client, uint32_t id)
|
|
{
|
|
struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
|
|
struct pw_permission *p;
|
|
uint32_t idx = id + 1;
|
|
|
|
if (id == PW_ID_ANY)
|
|
goto do_default;
|
|
|
|
if (!pw_array_check_index(&impl->permissions, idx, struct pw_permission))
|
|
goto do_default;
|
|
|
|
p = pw_array_get_unchecked(&impl->permissions, idx, struct pw_permission);
|
|
if (p->permissions == PW_PERM_INVALID)
|
|
goto do_default;
|
|
|
|
return p;
|
|
|
|
do_default:
|
|
return pw_array_get_unchecked(&impl->permissions, 0, struct pw_permission);
|
|
}
|
|
|
|
static struct pw_permission *ensure_permissions(struct pw_impl_client *client, uint32_t id)
|
|
{
|
|
struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
|
|
struct pw_permission *p;
|
|
uint32_t idx = id + 1;
|
|
size_t len, i;
|
|
|
|
len = pw_array_get_len(&impl->permissions, struct pw_permission);
|
|
if (len <= idx) {
|
|
size_t diff = idx - len + 1;
|
|
|
|
p = pw_array_add(&impl->permissions, diff * sizeof(struct pw_permission));
|
|
if (p == NULL)
|
|
return NULL;
|
|
|
|
for (i = 0; i < diff; i++) {
|
|
p[i] = PW_PERMISSION_INIT(len + i - 1, PW_PERM_INVALID);
|
|
}
|
|
}
|
|
p = pw_array_get_unchecked(&impl->permissions, idx, struct pw_permission);
|
|
return p;
|
|
}
|
|
|
|
/** \endcond */
|
|
|
|
static uint32_t
|
|
client_permission_func(struct pw_global *global,
|
|
struct pw_impl_client *client, void *data)
|
|
{
|
|
struct pw_permission *p = find_permission(client, global->id);
|
|
return p->permissions;
|
|
}
|
|
|
|
struct error_data {
|
|
uint32_t id;
|
|
int res;
|
|
const char *error;
|
|
};
|
|
|
|
static int error_resource(void *object, void *data)
|
|
{
|
|
struct pw_resource *r = object;
|
|
struct error_data *d = data;
|
|
if (r && r->bound_id == d->id)
|
|
pw_resource_error(r, d->res, d->error);
|
|
return 0;
|
|
}
|
|
|
|
static int client_error(void *object, uint32_t id, int res, const char *error)
|
|
{
|
|
struct pw_resource *resource = object;
|
|
struct resource_data *data = pw_resource_get_user_data(resource);
|
|
struct pw_impl_client *client = data->client;
|
|
struct error_data d = { id, res, error };
|
|
|
|
pw_log_debug(NAME" %p: error for global %d", client, id);
|
|
pw_map_for_each(&client->objects, error_resource, &d);
|
|
return 0;
|
|
}
|
|
|
|
static int update_properties(struct pw_impl_client *client, const struct spa_dict *dict, bool filter)
|
|
{
|
|
struct pw_resource *resource;
|
|
int changed = 0;
|
|
uint32_t i;
|
|
const char *old;
|
|
|
|
for (i = 0; i < dict->n_items; i++) {
|
|
if (filter && strstr(dict->items[i].key, "pipewire.") == dict->items[i].key &&
|
|
(old = pw_properties_get(client->properties, dict->items[i].key)) != NULL &&
|
|
(dict->items[i].value == NULL || strcmp(old, dict->items[i].value) != 0)) {
|
|
pw_log_warn(NAME" %p: refuse property update '%s' from '%s' to '%s'",
|
|
client, dict->items[i].key, old,
|
|
dict->items[i].value);
|
|
continue;
|
|
}
|
|
changed += pw_properties_set(client->properties, dict->items[i].key, dict->items[i].value);
|
|
}
|
|
client->info.props = &client->properties->dict;
|
|
|
|
pw_log_debug(NAME" %p: updated %d properties", client, changed);
|
|
|
|
if (!changed)
|
|
return 0;
|
|
|
|
client->info.change_mask |= PW_CLIENT_CHANGE_MASK_PROPS;
|
|
|
|
pw_impl_client_emit_info_changed(client, &client->info);
|
|
|
|
if (client->global)
|
|
spa_list_for_each(resource, &client->global->resource_list, link)
|
|
pw_client_resource_info(resource, &client->info);
|
|
|
|
client->info.change_mask = 0;
|
|
|
|
return changed;
|
|
}
|
|
|
|
static void update_busy(struct pw_impl_client *client)
|
|
{
|
|
struct pw_permission *def;
|
|
def = find_permission(client, PW_ID_CORE);
|
|
pw_impl_client_set_busy(client, (def->permissions & PW_PERM_R) ? false : true);
|
|
}
|
|
|
|
static int finish_register(struct pw_impl_client *client)
|
|
{
|
|
struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
|
|
struct pw_impl_client *current;
|
|
const char *keys[] = {
|
|
PW_KEY_ACCESS,
|
|
PW_KEY_CLIENT_ACCESS,
|
|
PW_KEY_APP_NAME,
|
|
NULL
|
|
};
|
|
if (impl->registered)
|
|
return 0;
|
|
|
|
impl->registered = true;
|
|
|
|
current = client->context->current_client;
|
|
client->context->current_client = NULL;
|
|
pw_context_emit_check_access(client->context, client);
|
|
client->context->current_client = current;
|
|
|
|
update_busy(client);
|
|
|
|
pw_global_update_keys(client->global, client->info.props, keys);
|
|
pw_global_register(client->global);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int client_update_properties(void *object, const struct spa_dict *props)
|
|
{
|
|
struct pw_resource *resource = object;
|
|
struct resource_data *data = pw_resource_get_user_data(resource);
|
|
struct pw_impl_client *client = data->client;
|
|
int res = update_properties(client, props, true);
|
|
finish_register(client);
|
|
return res;
|
|
}
|
|
|
|
static int client_get_permissions(void *object, uint32_t index, uint32_t num)
|
|
{
|
|
struct pw_resource *resource = object;
|
|
struct resource_data *data = pw_resource_get_user_data(resource);
|
|
struct pw_impl_client *client = data->client;
|
|
struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
|
|
size_t len;
|
|
|
|
len = pw_array_get_len(&impl->permissions, struct pw_permission);
|
|
if ((size_t)index >= len)
|
|
num = 0;
|
|
else if ((size_t)index + (size_t)num >= len)
|
|
num = len - index;
|
|
|
|
pw_client_resource_permissions(resource, index,
|
|
num, pw_array_get_unchecked(&impl->permissions, index, struct pw_permission));
|
|
return 0;
|
|
}
|
|
|
|
static int client_update_permissions(void *object,
|
|
uint32_t n_permissions, const struct pw_permission *permissions)
|
|
{
|
|
struct pw_resource *resource = object;
|
|
struct resource_data *data = pw_resource_get_user_data(resource);
|
|
struct pw_impl_client *client = data->client;
|
|
return pw_impl_client_update_permissions(client, n_permissions, permissions);
|
|
}
|
|
|
|
static const struct pw_client_methods client_methods = {
|
|
PW_VERSION_CLIENT_METHODS,
|
|
.error = client_error,
|
|
.update_properties = client_update_properties,
|
|
.get_permissions = client_get_permissions,
|
|
.update_permissions = client_update_permissions
|
|
};
|
|
|
|
static void client_unbind_func(void *data)
|
|
{
|
|
struct pw_resource *resource = data;
|
|
if (resource->id == 1)
|
|
resource->client->client_resource = NULL;
|
|
}
|
|
|
|
static const struct pw_resource_events resource_events = {
|
|
PW_VERSION_RESOURCE_EVENTS,
|
|
.destroy = client_unbind_func,
|
|
};
|
|
|
|
static int
|
|
global_bind(void *_data, struct pw_impl_client *client, uint32_t permissions,
|
|
uint32_t version, uint32_t id)
|
|
{
|
|
struct pw_impl_client *this = _data;
|
|
struct pw_global *global = this->global;
|
|
struct pw_resource *resource;
|
|
struct resource_data *data;
|
|
|
|
resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data));
|
|
if (resource == NULL)
|
|
goto error_resource;
|
|
|
|
data = pw_resource_get_user_data(resource);
|
|
data->client = this;
|
|
pw_resource_add_listener(resource,
|
|
&data->resource_listener,
|
|
&resource_events, resource);
|
|
pw_resource_add_object_listener(resource,
|
|
&data->object_listener,
|
|
&client_methods, resource);
|
|
|
|
pw_log_debug(NAME" %p: bound to %d", this, resource->id);
|
|
pw_global_add_resource(global, resource);
|
|
|
|
if (resource->id == 1)
|
|
client->client_resource = resource;
|
|
|
|
this->info.change_mask = PW_CLIENT_CHANGE_MASK_ALL;
|
|
pw_client_resource_info(resource, &this->info);
|
|
this->info.change_mask = 0;
|
|
|
|
return 0;
|
|
|
|
error_resource:
|
|
pw_log_error(NAME" %p: can't create client resource: %m", this);
|
|
return -errno;
|
|
}
|
|
|
|
static void pool_added(void *data, struct pw_memblock *block)
|
|
{
|
|
struct impl *impl = data;
|
|
struct pw_impl_client *client = &impl->this;
|
|
|
|
pw_log_debug(NAME" %p: added block %d", client, block->id);
|
|
if (client->core_resource) {
|
|
pw_core_resource_add_mem(client->core_resource,
|
|
block->id, block->type, block->fd,
|
|
block->flags & PW_MEMBLOCK_FLAG_READWRITE);
|
|
}
|
|
}
|
|
|
|
static void pool_removed(void *data, struct pw_memblock *block)
|
|
{
|
|
struct impl *impl = data;
|
|
struct pw_impl_client *client = &impl->this;
|
|
pw_log_debug(NAME" %p: removed block %d", client, block->id);
|
|
if (client->core_resource)
|
|
pw_core_resource_remove_mem(client->core_resource, block->id);
|
|
}
|
|
|
|
static const struct pw_mempool_events pool_events = {
|
|
PW_VERSION_MEMPOOL_EVENTS,
|
|
.added = pool_added,
|
|
.removed = pool_removed,
|
|
};
|
|
|
|
static void
|
|
context_global_removed(void *data, struct pw_global *global)
|
|
{
|
|
struct impl *impl = data;
|
|
struct pw_impl_client *client = &impl->this;
|
|
struct pw_permission *p;
|
|
|
|
p = find_permission(client, global->id);
|
|
pw_log_debug(NAME" %p: global %d removed, %p", client, global->id, p);
|
|
if (p->id != PW_ID_ANY)
|
|
p->permissions = PW_PERM_INVALID;
|
|
}
|
|
|
|
static const struct pw_context_events context_events = {
|
|
PW_VERSION_CONTEXT_EVENTS,
|
|
.global_removed = context_global_removed,
|
|
};
|
|
|
|
/** Make a new client object
|
|
*
|
|
* \param context a \ref pw_context object to register the client with
|
|
* \param ucred a ucred structure or NULL when unknown
|
|
* \param properties optional client properties, ownership is taken
|
|
* \return a newly allocated client object
|
|
*
|
|
* \memberof pw_impl_client
|
|
*/
|
|
SPA_EXPORT
|
|
struct pw_impl_client *pw_context_create_client(struct pw_impl_core *core,
|
|
struct pw_protocol *protocol,
|
|
struct pw_properties *properties,
|
|
size_t user_data_size)
|
|
{
|
|
struct pw_impl_client *this;
|
|
struct impl *impl;
|
|
struct pw_permission *p;
|
|
int res;
|
|
|
|
impl = calloc(1, sizeof(struct impl) + user_data_size);
|
|
if (impl == NULL) {
|
|
res = -errno;
|
|
goto error_cleanup;
|
|
}
|
|
|
|
this = &impl->this;
|
|
pw_log_debug(NAME" %p: new", this);
|
|
|
|
this->context = core->context;
|
|
this->core = core;
|
|
this->protocol = protocol;
|
|
|
|
if (properties == NULL)
|
|
properties = pw_properties_new(NULL, NULL);
|
|
if (properties == NULL) {
|
|
res = -errno;
|
|
goto error_free;
|
|
}
|
|
|
|
pw_array_init(&impl->permissions, 1024);
|
|
p = pw_array_add(&impl->permissions, sizeof(struct pw_permission));
|
|
if (p == NULL) {
|
|
res = -errno;
|
|
goto error_clear_array;
|
|
}
|
|
p->id = PW_ID_ANY;
|
|
p->permissions = 0;
|
|
|
|
this->pool = pw_mempool_new(NULL);
|
|
if (this->pool == NULL) {
|
|
res = -errno;
|
|
goto error_clear_array;
|
|
}
|
|
pw_mempool_add_listener(this->pool, &impl->pool_listener, &pool_events, impl);
|
|
|
|
this->properties = properties;
|
|
this->permission_func = client_permission_func;
|
|
this->permission_data = impl;
|
|
|
|
if (user_data_size > 0)
|
|
this->user_data = SPA_MEMBER(impl, sizeof(struct impl), void);
|
|
|
|
spa_hook_list_init(&this->listener_list);
|
|
|
|
pw_map_init(&this->objects, 0, 32);
|
|
|
|
pw_context_add_listener(this->context, &impl->context_listener, &context_events, impl);
|
|
|
|
this->info.props = &this->properties->dict;
|
|
|
|
return this;
|
|
|
|
error_clear_array:
|
|
pw_array_clear(&impl->permissions);
|
|
error_free:
|
|
free(impl);
|
|
error_cleanup:
|
|
if (properties)
|
|
pw_properties_free(properties);
|
|
errno = -res;
|
|
return NULL;
|
|
}
|
|
|
|
static void global_destroy(void *object)
|
|
{
|
|
struct pw_impl_client *client = object;
|
|
spa_hook_remove(&client->global_listener);
|
|
client->global = NULL;
|
|
pw_impl_client_destroy(client);
|
|
}
|
|
|
|
static const struct pw_global_events global_events = {
|
|
PW_VERSION_GLOBAL_EVENTS,
|
|
.destroy = global_destroy,
|
|
};
|
|
|
|
SPA_EXPORT
|
|
int pw_impl_client_register(struct pw_impl_client *client,
|
|
struct pw_properties *properties)
|
|
{
|
|
struct pw_context *context = client->context;
|
|
const char *keys[] = {
|
|
PW_KEY_MODULE_ID,
|
|
PW_KEY_PROTOCOL,
|
|
PW_KEY_SEC_PID,
|
|
PW_KEY_SEC_UID,
|
|
PW_KEY_SEC_GID,
|
|
PW_KEY_SEC_LABEL,
|
|
NULL
|
|
};
|
|
|
|
if (client->registered)
|
|
goto error_existed;
|
|
|
|
pw_log_debug(NAME" %p: register", client);
|
|
|
|
client->global = pw_global_new(context,
|
|
PW_TYPE_INTERFACE_Client,
|
|
PW_VERSION_CLIENT,
|
|
properties,
|
|
global_bind,
|
|
client);
|
|
if (client->global == NULL)
|
|
return -errno;
|
|
|
|
spa_list_append(&context->client_list, &client->link);
|
|
client->registered = true;
|
|
|
|
client->info.id = client->global->id;
|
|
pw_properties_setf(client->properties, PW_KEY_OBJECT_ID, "%d", client->info.id);
|
|
client->info.props = &client->properties->dict;
|
|
pw_global_add_listener(client->global, &client->global_listener, &global_events, client);
|
|
|
|
pw_global_update_keys(client->global, client->info.props, keys);
|
|
|
|
pw_impl_client_emit_initialized(client);
|
|
|
|
return 0;
|
|
|
|
error_existed:
|
|
if (properties)
|
|
pw_properties_free(properties);
|
|
return -EEXIST;
|
|
}
|
|
|
|
SPA_EXPORT
|
|
struct pw_context *pw_impl_client_get_context(struct pw_impl_client *client)
|
|
{
|
|
return client->core->context;
|
|
}
|
|
|
|
SPA_EXPORT
|
|
struct pw_protocol *pw_impl_client_get_protocol(struct pw_impl_client *client)
|
|
{
|
|
return client->protocol;
|
|
}
|
|
|
|
SPA_EXPORT
|
|
struct pw_resource *pw_impl_client_get_core_resource(struct pw_impl_client *client)
|
|
{
|
|
return client->core_resource;
|
|
}
|
|
|
|
SPA_EXPORT
|
|
struct pw_resource *pw_impl_client_find_resource(struct pw_impl_client *client, uint32_t id)
|
|
{
|
|
return pw_map_lookup(&client->objects, id);
|
|
}
|
|
|
|
SPA_EXPORT
|
|
struct pw_global *pw_impl_client_get_global(struct pw_impl_client *client)
|
|
{
|
|
return client->global;
|
|
}
|
|
|
|
SPA_EXPORT
|
|
const struct pw_properties *pw_impl_client_get_properties(struct pw_impl_client *client)
|
|
{
|
|
return client->properties;
|
|
}
|
|
|
|
SPA_EXPORT
|
|
void *pw_impl_client_get_user_data(struct pw_impl_client *client)
|
|
{
|
|
return client->user_data;
|
|
}
|
|
|
|
static int destroy_resource(void *object, void *data)
|
|
{
|
|
if (object)
|
|
pw_resource_destroy(object);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Destroy a client object
|
|
*
|
|
* \param client the client to destroy
|
|
*
|
|
* \memberof pw_impl_client
|
|
*/
|
|
SPA_EXPORT
|
|
void pw_impl_client_destroy(struct pw_impl_client *client)
|
|
{
|
|
struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
|
|
|
|
pw_log_debug(NAME" %p: destroy", client);
|
|
pw_impl_client_emit_destroy(client);
|
|
|
|
spa_hook_remove(&impl->context_listener);
|
|
|
|
if (client->registered)
|
|
spa_list_remove(&client->link);
|
|
|
|
pw_map_for_each(&client->objects, destroy_resource, client);
|
|
|
|
if (client->global) {
|
|
spa_hook_remove(&client->global_listener);
|
|
pw_global_destroy(client->global);
|
|
}
|
|
|
|
pw_log_debug(NAME" %p: free", impl);
|
|
pw_impl_client_emit_free(client);
|
|
|
|
pw_map_clear(&client->objects);
|
|
pw_array_clear(&impl->permissions);
|
|
pw_mempool_destroy(client->pool);
|
|
|
|
pw_properties_free(client->properties);
|
|
|
|
free(impl);
|
|
}
|
|
|
|
SPA_EXPORT
|
|
void pw_impl_client_add_listener(struct pw_impl_client *client,
|
|
struct spa_hook *listener,
|
|
const struct pw_impl_client_events *events,
|
|
void *data)
|
|
{
|
|
spa_hook_list_append(&client->listener_list, listener, events, data);
|
|
}
|
|
|
|
SPA_EXPORT
|
|
const struct pw_client_info *pw_impl_client_get_info(struct pw_impl_client *client)
|
|
{
|
|
return &client->info;
|
|
}
|
|
|
|
/** Update client properties
|
|
*
|
|
* \param client the client
|
|
* \param dict a \ref spa_dict with properties
|
|
*
|
|
* Add all properties in \a dict to the client properties. Existing
|
|
* properties are overwritten. Items can be removed by setting the value
|
|
* to NULL.
|
|
*
|
|
* \memberof pw_impl_client
|
|
*/
|
|
SPA_EXPORT
|
|
int pw_impl_client_update_properties(struct pw_impl_client *client, const struct spa_dict *dict)
|
|
{
|
|
int res = update_properties(client, dict, false);
|
|
finish_register(client);
|
|
return res;
|
|
}
|
|
|
|
SPA_EXPORT
|
|
int pw_impl_client_update_permissions(struct pw_impl_client *client,
|
|
uint32_t n_permissions, const struct pw_permission *permissions)
|
|
{
|
|
struct pw_impl_core *core = client->core;
|
|
struct pw_context *context = core->context;
|
|
struct pw_permission *def;
|
|
uint32_t i;
|
|
|
|
if ((def = find_permission(client, PW_ID_ANY)) == NULL)
|
|
return -EIO;
|
|
|
|
for (i = 0; i < n_permissions; i++) {
|
|
struct pw_permission *p;
|
|
uint32_t old_perm, new_perm;
|
|
struct pw_global *global;
|
|
|
|
if (permissions[i].id == PW_ID_ANY) {
|
|
old_perm = def->permissions;
|
|
new_perm = permissions[i].permissions;
|
|
|
|
if (context->current_client == client)
|
|
new_perm &= old_perm;
|
|
|
|
pw_log_debug(NAME" %p: set default permissions %08x -> %08x",
|
|
client, old_perm, new_perm);
|
|
|
|
def->permissions = new_perm;
|
|
|
|
spa_list_for_each(global, &context->global_list, link) {
|
|
if (global->id == client->info.id)
|
|
continue;
|
|
p = find_permission(client, global->id);
|
|
if (p->id != PW_ID_ANY)
|
|
continue;
|
|
pw_global_update_permissions(global, client, old_perm, new_perm);
|
|
}
|
|
}
|
|
else {
|
|
struct pw_global *global;
|
|
|
|
global = pw_context_find_global(context, permissions[i].id);
|
|
if (global == NULL || global->id != permissions[i].id) {
|
|
pw_log_warn(NAME" %p: invalid global %d", client, permissions[i].id);
|
|
continue;
|
|
}
|
|
p = ensure_permissions(client, permissions[i].id);
|
|
if (p == NULL) {
|
|
pw_log_warn(NAME" %p: can't ensure permission: %m", client);
|
|
continue;
|
|
}
|
|
old_perm = p->permissions == PW_PERM_INVALID ? def->permissions : p->permissions;
|
|
new_perm = permissions[i].permissions;
|
|
|
|
if (context->current_client == client)
|
|
new_perm &= old_perm;
|
|
|
|
pw_log_debug(NAME" %p: set global %d permissions %08x -> %08x",
|
|
client, global->id, old_perm, new_perm);
|
|
|
|
p->permissions = new_perm;
|
|
pw_global_update_permissions(global, client, old_perm, new_perm);
|
|
}
|
|
}
|
|
update_busy(client);
|
|
return 0;
|
|
}
|
|
|
|
SPA_EXPORT
|
|
void pw_impl_client_set_busy(struct pw_impl_client *client, bool busy)
|
|
{
|
|
if (client->busy != busy) {
|
|
pw_log_debug(NAME" %p: busy %d", client, busy);
|
|
client->busy = busy;
|
|
pw_impl_client_emit_busy_changed(client, busy);
|
|
}
|
|
}
|
|
SPA_EXPORT
|
|
int pw_impl_client_check_permissions(struct pw_impl_client *client,
|
|
uint32_t global_id, uint32_t permissions)
|
|
{
|
|
struct pw_context *context = client->context;
|
|
struct pw_global *global;
|
|
uint32_t perms;
|
|
|
|
if ((global = pw_context_find_global(context, global_id)) == NULL)
|
|
return -ENOENT;
|
|
|
|
perms = pw_global_get_permissions(global, client);
|
|
if ((perms & permissions) != permissions)
|
|
return -EPERM;
|
|
|
|
return 0;
|
|
}
|