mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-07 13:30:09 -05:00
access: rework access checks
Remove the access struct. Allow for the access module to override any method of a resource to do additional checks.
This commit is contained in:
parent
2c1245f8ef
commit
611ce2151e
14 changed files with 192 additions and 300 deletions
|
|
@ -528,11 +528,18 @@ struct pw_client_node_events {
|
|||
* The transport area is used to exchange real-time commands between
|
||||
* the client and the server.
|
||||
*
|
||||
* \param readfd fd for signal data can be read
|
||||
* \param writefd fd for signal data can be written
|
||||
* \param memfd the memory fd of the area
|
||||
* \param offset the offset to map
|
||||
* \param size the size to map
|
||||
*/
|
||||
void (*transport) (void *object, int readfd, int writefd, int memfd, uint32_t offset, uint32_t size);
|
||||
void (*transport) (void *object,
|
||||
int readfd,
|
||||
int writefd,
|
||||
int memfd,
|
||||
uint32_t offset,
|
||||
uint32_t size);
|
||||
};
|
||||
|
||||
#define pw_client_node_notify_set_props(r,...) ((struct pw_client_node_events*)r->iface->events)->props(r,__VA_ARGS__)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@
|
|||
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include "pipewire/client/interfaces.h"
|
||||
#include "pipewire/client/utils.h"
|
||||
|
||||
#include "pipewire/server/core.h"
|
||||
#include "pipewire/server/module.h"
|
||||
|
||||
|
|
@ -43,7 +45,6 @@ struct impl {
|
|||
struct pw_listener global_removed;
|
||||
|
||||
struct spa_list client_list;
|
||||
struct pw_access access;
|
||||
|
||||
struct spa_source *dispatch_event;
|
||||
};
|
||||
|
|
@ -53,15 +54,23 @@ struct client_info {
|
|||
struct spa_list link;
|
||||
struct pw_client *client;
|
||||
bool is_sandboxed;
|
||||
const struct pw_core_methods *old_methods;
|
||||
struct pw_core_methods core_methods;
|
||||
struct spa_list async_pending;
|
||||
struct pw_listener resource_added;
|
||||
struct pw_listener resource_removed;
|
||||
};
|
||||
|
||||
struct async_pending {
|
||||
struct spa_list link;
|
||||
bool handled;
|
||||
struct client_info *info;
|
||||
bool handled;
|
||||
char *handle;
|
||||
struct pw_access_data *access_data;
|
||||
struct pw_resource *resource;
|
||||
char *factory_name;
|
||||
char *name;
|
||||
struct pw_properties *properties;
|
||||
uint32_t new_id;
|
||||
};
|
||||
|
||||
static struct client_info *find_client_info(struct impl *impl, struct pw_client *client)
|
||||
|
|
@ -106,44 +115,28 @@ static struct async_pending *find_pending(struct client_info *cinfo, const char
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void free_pending(struct pw_access_data *d)
|
||||
static void free_pending(struct async_pending *p)
|
||||
{
|
||||
struct async_pending *p = d->user_data;
|
||||
|
||||
if (!p->handled)
|
||||
close_request(p);
|
||||
|
||||
pw_log_debug("pending %p: handle %s", p, p->handle);
|
||||
spa_list_remove(&p->link);
|
||||
free(p->handle);
|
||||
}
|
||||
|
||||
static void
|
||||
add_pending(struct client_info *cinfo, const char *handle, struct pw_access_data *access_data)
|
||||
{
|
||||
struct async_pending *p;
|
||||
struct pw_access_data *ad;
|
||||
|
||||
ad = access_data->async_start(access_data, sizeof(struct async_pending));
|
||||
ad->free = free_pending;
|
||||
|
||||
p = ad->user_data;
|
||||
p->info = cinfo;
|
||||
p->handle = strdup(handle);
|
||||
p->access_data = ad;
|
||||
p->handled = false;
|
||||
pw_log_debug("pending %p: handle %s", p, handle);
|
||||
|
||||
spa_list_insert(cinfo->async_pending.prev, &p->link);
|
||||
free(p->factory_name);
|
||||
free(p->name);
|
||||
if (p->properties)
|
||||
pw_properties_free(p->properties);
|
||||
free(p);
|
||||
}
|
||||
|
||||
static void client_info_free(struct client_info *cinfo)
|
||||
{
|
||||
struct async_pending *p, *tmp;
|
||||
|
||||
spa_list_for_each_safe(p, tmp, &cinfo->async_pending, link) {
|
||||
p->access_data->complete(p->access_data, SPA_RESULT_NO_PERMISSION);
|
||||
}
|
||||
spa_list_for_each_safe(p, tmp, &cinfo->async_pending, link)
|
||||
free_pending(p);
|
||||
|
||||
spa_list_remove(&cinfo->link);
|
||||
free(cinfo);
|
||||
}
|
||||
|
|
@ -221,8 +214,8 @@ check_global_owner(struct pw_core *core, struct pw_client *client, struct pw_glo
|
|||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
do_view_global(struct pw_access *access, struct pw_client *client, struct pw_global *global)
|
||||
static bool
|
||||
do_global_filter(struct pw_global *global, struct pw_client *client, void *data)
|
||||
{
|
||||
if (global->type == client->core->type.link) {
|
||||
struct pw_link *link = global->object;
|
||||
|
|
@ -230,15 +223,15 @@ do_view_global(struct pw_access *access, struct pw_client *client, struct pw_glo
|
|||
/* we must be able to see both nodes */
|
||||
if (link->output
|
||||
&& !check_global_owner(client->core, client, link->output->node->global))
|
||||
return SPA_RESULT_ERROR;
|
||||
return false;
|
||||
|
||||
if (link->input
|
||||
&& !check_global_owner(client->core, client, link->input->node->global))
|
||||
return SPA_RESULT_ERROR;
|
||||
return false;
|
||||
} else if (!check_global_owner(client->core, client, global))
|
||||
return SPA_RESULT_ERROR;
|
||||
return false;
|
||||
|
||||
return SPA_RESULT_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
|
|
@ -250,7 +243,6 @@ portal_response(DBusConnection *connection, DBusMessage *msg, void *user_data)
|
|||
uint32_t response = 2;
|
||||
DBusError error;
|
||||
struct async_pending *p;
|
||||
struct pw_access_data *d;
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
|
|
@ -267,26 +259,39 @@ portal_response(DBusConnection *connection, DBusMessage *msg, void *user_data)
|
|||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
|
||||
p->handled = true;
|
||||
d = p->access_data;
|
||||
|
||||
pw_log_debug("portal check result: %d", response);
|
||||
|
||||
d->complete(d, response == 0 ? SPA_RESULT_OK : SPA_RESULT_NO_PERMISSION);
|
||||
if (response == 0) {
|
||||
cinfo->old_methods->create_node (p->resource,
|
||||
p->factory_name,
|
||||
p->name,
|
||||
&p->properties->dict,
|
||||
p->new_id);
|
||||
} else {
|
||||
pw_core_notify_error(cinfo->client->core_resource,
|
||||
p->resource->id, SPA_RESULT_NO_PERMISSION, "not allowed");
|
||||
|
||||
}
|
||||
free_pending(p);
|
||||
pw_client_set_busy(cinfo->client, false);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
do_create_node(struct pw_access *access,
|
||||
struct pw_access_data *data,
|
||||
const char *factory_name,
|
||||
const char *name,
|
||||
struct pw_properties *properties)
|
||||
|
||||
static void do_create_node(void *object,
|
||||
const char *factory_name,
|
||||
const char *name,
|
||||
const struct spa_dict *props,
|
||||
uint32_t new_id)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(access, struct impl, access);
|
||||
struct client_info *cinfo = find_client_info(impl, data->resource->client);
|
||||
struct pw_resource *resource = object;
|
||||
struct client_info *cinfo = resource->access_private;
|
||||
struct impl *impl = cinfo->impl;
|
||||
struct pw_client *client = resource->client;
|
||||
DBusMessage *m = NULL, *r = NULL;
|
||||
DBusError error;
|
||||
pid_t pid;
|
||||
|
|
@ -294,14 +299,15 @@ do_create_node(struct pw_access *access,
|
|||
DBusMessageIter dict_iter;
|
||||
const char *handle;
|
||||
const char *device;
|
||||
struct async_pending *p;
|
||||
|
||||
if (!cinfo->is_sandboxed) {
|
||||
data->complete(data, SPA_RESULT_OK);
|
||||
return SPA_RESULT_OK;
|
||||
cinfo->old_methods->create_node (object, factory_name, name, props, new_id);
|
||||
return;
|
||||
}
|
||||
if (strcmp(factory_name, "client-node") != 0) {
|
||||
data->complete(data, SPA_RESULT_NO_PERMISSION);
|
||||
return SPA_RESULT_NO_PERMISSION;
|
||||
pw_log_error("can only allow client-node");
|
||||
goto not_allowed;
|
||||
}
|
||||
|
||||
pw_log_info("ask portal for client %p", cinfo->client);
|
||||
|
|
@ -345,61 +351,93 @@ do_create_node(struct pw_access *access,
|
|||
|
||||
dbus_connection_add_filter(impl->bus, portal_response, cinfo, NULL);
|
||||
|
||||
add_pending(cinfo, handle, data);
|
||||
p = calloc(1, sizeof(struct async_pending));
|
||||
p->info = cinfo;
|
||||
p->handle = strdup(handle);
|
||||
p->handled = false;
|
||||
p->resource = resource;
|
||||
p->factory_name = strdup(factory_name);
|
||||
p->name = strdup(name);
|
||||
p->properties = props ? pw_properties_new_dict(props) : NULL;
|
||||
p->new_id = new_id;
|
||||
pw_client_set_busy(client, true);
|
||||
|
||||
return SPA_RESULT_RETURN_ASYNC(0);
|
||||
pw_log_debug("pending %p: handle %s", p, handle);
|
||||
spa_list_insert(cinfo->async_pending.prev, &p->link);
|
||||
|
||||
return;
|
||||
|
||||
no_method_call:
|
||||
pw_log_error("Failed to create message");
|
||||
return SPA_RESULT_NO_PERMISSION;
|
||||
goto not_allowed;
|
||||
message_failed:
|
||||
dbus_message_unref(m);
|
||||
return SPA_RESULT_NO_PERMISSION;
|
||||
goto not_allowed;
|
||||
send_failed:
|
||||
pw_log_error("Failed to call portal: %s", error.message);
|
||||
dbus_error_free(&error);
|
||||
dbus_message_unref(m);
|
||||
return SPA_RESULT_NO_PERMISSION;
|
||||
goto not_allowed;
|
||||
parse_failed:
|
||||
pw_log_error("Failed to parse AccessDevice result: %s", error.message);
|
||||
dbus_error_free(&error);
|
||||
dbus_message_unref(r);
|
||||
return SPA_RESULT_NO_PERMISSION;
|
||||
goto not_allowed;
|
||||
subscribe_failed:
|
||||
pw_log_error("Failed to subscribe to Request signal: %s", error.message);
|
||||
dbus_error_free(&error);
|
||||
return SPA_RESULT_NO_PERMISSION;
|
||||
goto not_allowed;
|
||||
not_allowed:
|
||||
pw_core_notify_error(client->core_resource,
|
||||
resource->id, SPA_RESULT_NO_PERMISSION, "not allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
do_create_link(struct pw_access *access,
|
||||
struct pw_access_data *data,
|
||||
static void
|
||||
do_create_link(void *object,
|
||||
uint32_t output_node_id,
|
||||
uint32_t output_port_id,
|
||||
uint32_t input_node_id,
|
||||
uint32_t input_port_id,
|
||||
const struct spa_format *filter,
|
||||
const struct pw_properties *props)
|
||||
const struct spa_dict *props,
|
||||
uint32_t new_id)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(access, struct impl, access);
|
||||
struct client_info *cinfo = find_client_info(impl, data->resource->client);
|
||||
int res;
|
||||
struct pw_resource *resource = object;
|
||||
struct client_info *cinfo = resource->access_private;
|
||||
struct pw_client *client = resource->client;
|
||||
|
||||
if (cinfo->is_sandboxed)
|
||||
res = SPA_RESULT_NO_PERMISSION;
|
||||
else
|
||||
res = SPA_RESULT_OK;
|
||||
|
||||
data->complete(data, res);
|
||||
return SPA_RESULT_OK;
|
||||
if (cinfo->is_sandboxed) {
|
||||
pw_core_notify_error(client->core_resource,
|
||||
resource->id, SPA_RESULT_NO_PERMISSION, "not allowed");
|
||||
return;
|
||||
}
|
||||
cinfo->old_methods->create_link (object,
|
||||
output_node_id,
|
||||
output_port_id,
|
||||
input_node_id,
|
||||
input_port_id,
|
||||
filter,
|
||||
props,
|
||||
new_id);
|
||||
}
|
||||
|
||||
static void on_resource_added(struct pw_listener *listener,
|
||||
struct pw_client *client,
|
||||
struct pw_resource *resource)
|
||||
{
|
||||
struct client_info *cinfo = SPA_CONTAINER_OF(listener, struct client_info, resource_added);
|
||||
struct impl *impl = cinfo->impl;
|
||||
|
||||
static struct pw_access access_checks = {
|
||||
do_view_global,
|
||||
do_create_node,
|
||||
do_create_link,
|
||||
};
|
||||
if (resource->type == impl->core->type.core) {
|
||||
cinfo->old_methods = resource->implementation;
|
||||
cinfo->core_methods = *cinfo->old_methods;
|
||||
resource->implementation = &cinfo->core_methods;
|
||||
resource->access_private = cinfo;
|
||||
cinfo->core_methods.create_node = do_create_node;
|
||||
cinfo->core_methods.create_link = do_create_link;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_global_added(struct pw_listener *listener, struct pw_core *core, struct pw_global *global)
|
||||
|
|
@ -417,6 +455,8 @@ on_global_added(struct pw_listener *listener, struct pw_core *core, struct pw_gl
|
|||
cinfo->is_sandboxed = true;
|
||||
spa_list_init(&cinfo->async_pending);
|
||||
|
||||
pw_signal_add(&client->resource_added, &cinfo->resource_added, on_resource_added);
|
||||
|
||||
spa_list_insert(impl->client_list.prev, &cinfo->link);
|
||||
|
||||
pw_log_debug("module %p: client %p added", impl, client);
|
||||
|
|
@ -624,7 +664,6 @@ static struct impl *module_new(struct pw_core *core, struct pw_properties *prope
|
|||
|
||||
impl->core = core;
|
||||
impl->properties = properties;
|
||||
impl->access = access_checks;
|
||||
|
||||
impl->bus = dbus_bus_get_private(DBUS_BUS_SESSION, &error);
|
||||
if (impl->bus == NULL)
|
||||
|
|
@ -640,12 +679,12 @@ static struct impl *module_new(struct pw_core *core, struct pw_properties *prope
|
|||
toggle_timeout, impl, NULL);
|
||||
dbus_connection_set_wakeup_main_function(impl->bus, wakeup_main, impl, NULL);
|
||||
|
||||
core->access = &impl->access;
|
||||
|
||||
spa_list_init(&impl->client_list);
|
||||
|
||||
pw_signal_add(&core->global_added, &impl->global_added, on_global_added);
|
||||
pw_signal_add(&core->global_removed, &impl->global_removed, on_global_removed);
|
||||
core->global_filter = do_global_filter;
|
||||
core->global_filter_data = impl;
|
||||
|
||||
return impl;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#include "pipewire/server/core.h"
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/* 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_ACCESS_H__
|
||||
#define __PIPEWIRE_ACCESS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PIPEWIRE_TYPE__Access "PipeWire:Object:Access"
|
||||
#define PIPEWIRE_TYPE_ACCESS_BASE PIPEWIRE_TYPE__Access ":"
|
||||
|
||||
#include <pipewire/client/sig.h>
|
||||
#include <pipewire/server/client.h>
|
||||
#include <pipewire/server/resource.h>
|
||||
|
||||
struct pw_access_data {
|
||||
struct pw_resource *resource;
|
||||
|
||||
void *(*async_start) (struct pw_access_data *data, size_t size);
|
||||
void (*complete) (struct pw_access_data *data, int res);
|
||||
void (*free) (struct pw_access_data *data);
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \struct pw_access
|
||||
*
|
||||
* PipeWire Access support struct.
|
||||
*/
|
||||
struct pw_access {
|
||||
int (*view_global) (struct pw_access *access,
|
||||
struct pw_client *client, struct pw_global *global);
|
||||
int (*create_node) (struct pw_access *access,
|
||||
struct pw_access_data *data,
|
||||
const char *factory_name,
|
||||
const char *name, struct pw_properties *properties);
|
||||
int (*create_link) (struct pw_access *access,
|
||||
struct pw_access_data *data,
|
||||
uint32_t output_node_id,
|
||||
uint32_t output_port_id,
|
||||
uint32_t input_node_id,
|
||||
uint32_t input_port_id,
|
||||
const struct spa_format *filter,
|
||||
const struct pw_properties *props);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PIPEWIRE_ACCESS_H__ */
|
||||
|
|
@ -1173,7 +1173,9 @@ struct pw_client_node *pw_client_node_new(struct pw_client *client,
|
|||
this->resource = pw_resource_new(client,
|
||||
id,
|
||||
client->core->type.client_node,
|
||||
this, (pw_destroy_t) client_node_resource_destroy);
|
||||
this,
|
||||
&client_node_methods,
|
||||
(pw_destroy_t) client_node_resource_destroy);
|
||||
if (this->resource == NULL)
|
||||
goto error_no_resource;
|
||||
|
||||
|
|
@ -1184,8 +1186,6 @@ struct pw_client_node *pw_client_node_new(struct pw_client *client,
|
|||
pw_signal_add(&this->node->loop_changed, &impl->loop_changed, on_loop_changed);
|
||||
pw_signal_add(&impl->core->global_added, &impl->global_added, on_global_added);
|
||||
|
||||
this->resource->implementation = &client_node_methods;
|
||||
|
||||
return this;
|
||||
|
||||
error_no_resource:
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ client_bind_func(struct pw_global *global, struct pw_client *client, uint32_t ve
|
|||
struct pw_client *this = global->object;
|
||||
struct pw_resource *resource;
|
||||
|
||||
resource = pw_resource_new(client, id, global->type, global->object, client_unbind_func);
|
||||
resource = pw_resource_new(client, id, global->type, global->object, NULL, client_unbind_func);
|
||||
if (resource == NULL)
|
||||
goto no_mem;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,19 +38,16 @@ struct impl {
|
|||
struct spa_support support[4];
|
||||
};
|
||||
|
||||
struct access_create_node {
|
||||
struct pw_access_data data;
|
||||
char *factory_name;
|
||||
char *name;
|
||||
struct pw_properties *properties;
|
||||
uint32_t new_id;
|
||||
bool async;
|
||||
};
|
||||
/** \endcond */
|
||||
|
||||
#define ACCESS_VIEW_GLOBAL(client,global) (client->core->access == NULL || \
|
||||
client->core->access->view_global (client->core->access, \
|
||||
client, global) == SPA_RESULT_OK)
|
||||
static bool pw_global_is_visible(struct pw_global *global,
|
||||
struct pw_client *client)
|
||||
{
|
||||
struct pw_core *core = client->core;
|
||||
|
||||
return (core->global_filter == NULL ||
|
||||
core->global_filter(global, client, core->global_filter_data));
|
||||
}
|
||||
|
||||
static void registry_bind(void *object, uint32_t id, uint32_t version, uint32_t new_id)
|
||||
{
|
||||
|
|
@ -66,7 +63,7 @@ static void registry_bind(void *object, uint32_t id, uint32_t version, uint32_t
|
|||
if (&global->link == &core->global_list)
|
||||
goto no_id;
|
||||
|
||||
if (!ACCESS_VIEW_GLOBAL(client, global))
|
||||
if (!pw_global_is_visible(global, client))
|
||||
goto no_id;
|
||||
|
||||
pw_log_debug("global %p: bind object id %d to %d", global, id, new_id);
|
||||
|
|
@ -118,16 +115,17 @@ static void core_get_registry(void *object, uint32_t new_id)
|
|||
|
||||
registry_resource = pw_resource_new(client,
|
||||
new_id,
|
||||
this->type.registry, this, destroy_registry_resource);
|
||||
this->type.registry,
|
||||
this,
|
||||
®istry_methods,
|
||||
destroy_registry_resource);
|
||||
if (registry_resource == NULL)
|
||||
goto no_mem;
|
||||
|
||||
registry_resource->implementation = ®istry_methods;
|
||||
|
||||
spa_list_insert(this->registry_resource_list.prev, ®istry_resource->link);
|
||||
|
||||
spa_list_for_each(global, &this->global_list, link) {
|
||||
if (ACCESS_VIEW_GLOBAL(client, global))
|
||||
if (pw_global_is_visible(global, client))
|
||||
pw_registry_notify_global(registry_resource,
|
||||
global->id,
|
||||
spa_type_map_get_type(this->type.map,
|
||||
|
|
@ -143,73 +141,6 @@ static void core_get_registry(void *object, uint32_t new_id)
|
|||
resource->id, SPA_RESULT_NO_MEMORY, "no memory");
|
||||
}
|
||||
|
||||
static void *async_create_node_start(struct pw_access_data *data, size_t size)
|
||||
{
|
||||
struct access_create_node *d;
|
||||
struct pw_client *client = data->resource->client;
|
||||
|
||||
d = calloc(1, sizeof(struct access_create_node) + size);
|
||||
memcpy(d, data, sizeof(struct access_create_node));
|
||||
d->factory_name = strdup(d->factory_name);
|
||||
d->name = strdup(d->name);
|
||||
d->async = true;
|
||||
d->data.user_data = SPA_MEMBER(d, sizeof(struct access_create_node), void);
|
||||
|
||||
pw_client_set_busy(client, true);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static void async_create_node_free(struct pw_access_data *data)
|
||||
{
|
||||
struct access_create_node *d = (struct access_create_node *) data;
|
||||
|
||||
if (d->properties)
|
||||
pw_properties_free(d->properties);
|
||||
if (d->async) {
|
||||
if (d->data.free)
|
||||
d->data.free(&d->data);
|
||||
free(d->factory_name);
|
||||
free(d->name);
|
||||
free(d);
|
||||
}
|
||||
}
|
||||
|
||||
static void async_create_node_complete(struct pw_access_data *data, int res)
|
||||
{
|
||||
struct access_create_node *d = (struct access_create_node *) data;
|
||||
struct pw_resource *resource = d->data.resource;
|
||||
struct pw_client *client = resource->client;
|
||||
struct pw_node_factory *factory;
|
||||
|
||||
if (res != SPA_RESULT_OK)
|
||||
goto denied;
|
||||
|
||||
factory = pw_core_find_node_factory(client->core, d->factory_name);
|
||||
if (factory == NULL)
|
||||
goto no_factory;
|
||||
|
||||
/* error will be posted */
|
||||
pw_node_factory_create_node(factory, client, d->name, d->properties, d->new_id);
|
||||
d->properties = NULL;
|
||||
|
||||
goto done;
|
||||
|
||||
no_factory:
|
||||
pw_log_error("can't find node factory");
|
||||
pw_core_notify_error(client->core_resource,
|
||||
resource->id, SPA_RESULT_INVALID_ARGUMENTS, "unknown factory name");
|
||||
goto done;
|
||||
denied:
|
||||
pw_log_error("create node refused %d", res);
|
||||
pw_core_notify_error(client->core_resource,
|
||||
resource->id, SPA_RESULT_NO_PERMISSION, "operation not allowed");
|
||||
done:
|
||||
async_create_node_free(&d->data);
|
||||
pw_client_set_busy(client, false);
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
core_create_node(void *object,
|
||||
const char *factory_name,
|
||||
|
|
@ -219,47 +150,39 @@ core_create_node(void *object,
|
|||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_client *client = resource->client;
|
||||
int i;
|
||||
struct pw_node_factory *factory;
|
||||
struct pw_properties *properties;
|
||||
struct access_create_node access_data;
|
||||
int res;
|
||||
int i;
|
||||
|
||||
factory = pw_core_find_node_factory(client->core, factory_name);
|
||||
if (factory == NULL)
|
||||
goto no_factory;
|
||||
|
||||
properties = pw_properties_new(NULL, NULL);
|
||||
if (properties == NULL)
|
||||
goto no_mem;
|
||||
|
||||
for (i = 0; i < props->n_items; i++) {
|
||||
for (i = 0; i < props->n_items; i++)
|
||||
pw_properties_set(properties, props->items[i].key, props->items[i].value);
|
||||
}
|
||||
|
||||
access_data.data.resource = resource;
|
||||
access_data.data.async_start = async_create_node_start;
|
||||
access_data.data.complete = async_create_node_complete;
|
||||
access_data.data.free = NULL;
|
||||
access_data.factory_name = (char *) factory_name;
|
||||
access_data.name = (char *) name;
|
||||
access_data.properties = properties;
|
||||
access_data.new_id = new_id;
|
||||
access_data.async = false;
|
||||
/* error will be posted */
|
||||
pw_node_factory_create_node(factory, client, name, properties, new_id);
|
||||
properties = NULL;
|
||||
|
||||
if (client->core->access) {
|
||||
res = client->core->access->create_node(client->core->access,
|
||||
&access_data.data,
|
||||
factory_name,
|
||||
name,
|
||||
properties);
|
||||
} else {
|
||||
res = SPA_RESULT_OK;
|
||||
}
|
||||
if (!SPA_RESULT_IS_ASYNC(res))
|
||||
async_create_node_complete(&access_data.data, res);
|
||||
done:
|
||||
return;
|
||||
|
||||
no_factory:
|
||||
pw_log_error("can't find node factory");
|
||||
pw_core_notify_error(client->core_resource,
|
||||
resource->id, SPA_RESULT_INVALID_ARGUMENTS, "unknown factory name");
|
||||
goto done;
|
||||
|
||||
no_mem:
|
||||
pw_log_error("can't create client node");
|
||||
pw_log_error("can't create properties");
|
||||
pw_core_notify_error(client->core_resource,
|
||||
resource->id, SPA_RESULT_NO_MEMORY, "no memory");
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -294,7 +217,7 @@ static void core_update_types(void *object, uint32_t first_id, uint32_t n_types,
|
|||
}
|
||||
}
|
||||
|
||||
static struct pw_core_methods core_methods = {
|
||||
static const struct pw_core_methods core_methods = {
|
||||
&core_update_types,
|
||||
&core_sync,
|
||||
&core_get_registry,
|
||||
|
|
@ -316,12 +239,10 @@ core_bind_func(struct pw_global *global, struct pw_client *client, uint32_t vers
|
|||
struct pw_core *this = global->object;
|
||||
struct pw_resource *resource;
|
||||
|
||||
resource = pw_resource_new(client, id, global->type, global->object, core_unbind_func);
|
||||
resource = pw_resource_new(client, id, global->type, global->object, &core_methods, core_unbind_func);
|
||||
if (resource == NULL)
|
||||
goto no_mem;
|
||||
|
||||
resource->implementation = &core_methods;
|
||||
|
||||
spa_list_insert(this->resource_list.prev, &resource->link);
|
||||
client->core_resource = resource;
|
||||
|
||||
|
|
@ -483,8 +404,8 @@ pw_core_add_global(struct pw_core *core,
|
|||
pw_log_debug("global %p: new %u %s, owner %p", this, this->id, type_name, owner);
|
||||
|
||||
spa_list_for_each(registry, &core->registry_resource_list, link)
|
||||
if (ACCESS_VIEW_GLOBAL(registry->client, this))
|
||||
pw_registry_notify_global(registry, this->id, type_name, this->version);
|
||||
if (pw_global_is_visible(this, registry->client))
|
||||
pw_registry_notify_global(registry, this->id, type_name, this->version);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -533,8 +454,8 @@ void pw_global_destroy(struct pw_global *global)
|
|||
pw_signal_emit(&global->destroy_signal, global);
|
||||
|
||||
spa_list_for_each(registry, &core->registry_resource_list, link)
|
||||
if (ACCESS_VIEW_GLOBAL(registry->client, global))
|
||||
pw_registry_notify_global_remove(registry, global->id);
|
||||
if (pw_global_is_visible(global, registry->client))
|
||||
pw_registry_notify_global_remove(registry, global->id);
|
||||
|
||||
pw_map_remove(&core->objects, global->id);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ extern "C" {
|
|||
struct pw_global;
|
||||
|
||||
#include <pipewire/client/type.h>
|
||||
#include <pipewire/server/access.h>
|
||||
#include <pipewire/client/interfaces.h>
|
||||
|
||||
#include <pipewire/server/main-loop.h>
|
||||
#include <pipewire/server/data-loop.h>
|
||||
#include <pipewire/server/node.h>
|
||||
|
|
@ -87,6 +88,9 @@ struct pw_global;
|
|||
typedef int (*pw_bind_func_t) (struct pw_global *global,
|
||||
struct pw_client *client, uint32_t version, uint32_t id);
|
||||
|
||||
typedef bool (*pw_global_filter_func_t) (struct pw_global *global,
|
||||
struct pw_client *client, void *data);
|
||||
|
||||
/** \page page_global Global
|
||||
*
|
||||
* Global objects represent resources that are available on the server and
|
||||
|
|
@ -141,7 +145,9 @@ struct pw_core {
|
|||
struct pw_properties *properties; /**< properties of the core */
|
||||
|
||||
struct pw_type type; /**< type map and common types */
|
||||
struct pw_access *access; /**< access control checks */
|
||||
|
||||
pw_global_filter_func_t global_filter;
|
||||
void *global_filter_data;
|
||||
|
||||
struct pw_map objects; /**< map of known objects */
|
||||
|
||||
|
|
|
|||
|
|
@ -834,7 +834,7 @@ link_bind_func(struct pw_global *global, struct pw_client *client, uint32_t vers
|
|||
struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
|
||||
struct pw_resource *resource;
|
||||
|
||||
resource = pw_resource_new(client, id, global->type, global->object, link_unbind_func);
|
||||
resource = pw_resource_new(client, id, global->type, global->object, NULL, link_unbind_func);
|
||||
if (resource == NULL)
|
||||
goto no_mem;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
pipewirecore_headers = [
|
||||
'access.h',
|
||||
'client.h',
|
||||
'client-node.h',
|
||||
'command.h',
|
||||
|
|
@ -17,7 +16,6 @@ pipewirecore_headers = [
|
|||
]
|
||||
|
||||
pipewirecore_sources = [
|
||||
'access.c',
|
||||
'client.c',
|
||||
'client-node.c',
|
||||
'command.c',
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ module_bind_func(struct pw_global *global, struct pw_client *client, uint32_t ve
|
|||
struct pw_module *this = global->object;
|
||||
struct pw_resource *resource;
|
||||
|
||||
resource = pw_resource_new(client, id, global->type, global->object, NULL);
|
||||
resource = pw_resource_new(client, id, global->type, global->object, NULL, NULL);
|
||||
if (resource == NULL)
|
||||
goto no_mem;
|
||||
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@ node_bind_func(struct pw_global *global, struct pw_client *client, uint32_t vers
|
|||
struct pw_node *this = global->object;
|
||||
struct pw_resource *resource;
|
||||
|
||||
resource = pw_resource_new(client, id, global->type, global->object, node_unbind_func);
|
||||
resource = pw_resource_new(client, id, global->type, global->object, NULL, node_unbind_func);
|
||||
if (resource == NULL)
|
||||
goto no_mem;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,11 @@ struct impl {
|
|||
/** \endcond */
|
||||
|
||||
struct pw_resource *pw_resource_new(struct pw_client *client,
|
||||
uint32_t id, uint32_t type, void *object, pw_destroy_t destroy)
|
||||
uint32_t id,
|
||||
uint32_t type,
|
||||
void *object,
|
||||
const void *implementation,
|
||||
pw_destroy_t destroy)
|
||||
{
|
||||
struct impl *impl;
|
||||
struct pw_resource *this;
|
||||
|
|
@ -43,6 +47,7 @@ struct pw_resource *pw_resource_new(struct pw_client *client,
|
|||
this->client = client;
|
||||
this->type = type;
|
||||
this->object = object;
|
||||
this->implementation = implementation;
|
||||
this->destroy = destroy;
|
||||
|
||||
pw_signal_init(&this->destroy_signal);
|
||||
|
|
|
|||
|
|
@ -70,13 +70,19 @@ struct pw_resource {
|
|||
const struct pw_interface *iface; /**< protocol specific interface functions */
|
||||
const void *implementation; /**< implementation */
|
||||
|
||||
void *access_private; /**< private data for access control */
|
||||
|
||||
/** Emited when the resource is destroyed */
|
||||
PW_SIGNAL(destroy_signal, (struct pw_listener *listener, struct pw_resource *resource));
|
||||
};
|
||||
|
||||
struct pw_resource *
|
||||
pw_resource_new(struct pw_client *client,
|
||||
uint32_t id, uint32_t type, void *object, pw_destroy_t destroy);
|
||||
uint32_t id,
|
||||
uint32_t type,
|
||||
void *object,
|
||||
const void *implementation,
|
||||
pw_destroy_t destroy);
|
||||
|
||||
void
|
||||
pw_resource_destroy(struct pw_resource *resource);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue