From 6b6b827a3b9f81b8830ceb1af6aa60427ca4aaba Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 8 Aug 2017 15:01:36 +0200 Subject: [PATCH] Add resource override method Add an easy way to override a resource implementation and use this in the flatpak module. Add more methods --- spa/include/spa/list.h | 13 +- src/modules/module-client-node/client-node.c | 11 +- .../module-client-node/protocol-native.c | 1 - src/modules/module-flatpak.c | 203 ++++++++++-------- src/modules/module-protocol-native.c | 93 ++++---- src/modules/spa/spa-monitor.c | 4 +- src/pipewire/client.c | 14 +- src/pipewire/client.h | 28 ++- src/pipewire/core.c | 32 ++- src/pipewire/core.h | 8 +- src/pipewire/listener.h | 26 ++- src/pipewire/private.h | 2 +- src/pipewire/proxy.c | 4 +- src/pipewire/proxy.h | 9 +- src/pipewire/remote.c | 5 + src/pipewire/remote.h | 2 + src/pipewire/resource.c | 25 ++- src/pipewire/resource.h | 22 +- 18 files changed, 323 insertions(+), 179 deletions(-) diff --git a/spa/include/spa/list.h b/spa/include/spa/list.h index 492bb4847..e97fb30fa 100644 --- a/spa/include/spa/list.h +++ b/spa/include/spa/list.h @@ -67,18 +67,23 @@ static inline void spa_list_remove(struct spa_list *elem) #define spa_list_last(item, type, member) \ SPA_CONTAINER_OF((head)->prev, type, member) -#define spa_list_for_each(pos, head, member) \ - for (pos = SPA_CONTAINER_OF((head)->next, __typeof__(*pos), member); \ +#define spa_list_for_each_next(pos, head, curr, member) \ + for (pos = SPA_CONTAINER_OF((curr)->next, __typeof__(*pos), member); \ &pos->member != (head); \ pos = SPA_CONTAINER_OF(pos->member.next, __typeof__(*pos), member)) -#define spa_list_for_each_safe(pos, tmp, head, member) \ - for (pos = SPA_CONTAINER_OF((head)->next, __typeof__(*pos), member), \ +#define spa_list_for_each(pos, head, member) \ + spa_list_for_each_next(pos, head, head, member) \ + +#define spa_list_for_each_safe_next(pos, tmp, head, curr, member) \ + for (pos = SPA_CONTAINER_OF((curr)->next, __typeof__(*pos), member), \ tmp = SPA_CONTAINER_OF((pos)->member.next, __typeof__(*tmp), member); \ &pos->member != (head); \ pos = tmp, \ tmp = SPA_CONTAINER_OF(pos->member.next, __typeof__(*tmp), member)) +#define spa_list_for_each_safe(pos, tmp, head, member) \ + spa_list_for_each_safe_next(pos, tmp, head, head, member) \ #ifdef __cplusplus } /* extern "C" */ diff --git a/src/modules/module-client-node/client-node.c b/src/modules/module-client-node/client-node.c index 58487493d..702885d21 100644 --- a/src/modules/module-client-node/client-node.c +++ b/src/modules/module-client-node/client-node.c @@ -33,7 +33,6 @@ #include "spa/lib/format.h" #include "pipewire/pipewire.h" -#include "pipewire/private.h" #include "pipewire/interfaces.h" #include "pipewire/core.h" @@ -1136,7 +1135,11 @@ struct pw_client_node *pw_client_node_new(struct pw_resource *resource, { struct impl *impl; struct pw_client_node *this; - struct pw_core *core = resource->client->core; + struct pw_client *client = pw_resource_get_client(resource); + struct pw_core *core = pw_client_get_core(client); + const struct spa_support *support; + uint32_t n_support; + impl = calloc(1, sizeof(struct impl)); if (impl == NULL) @@ -1149,7 +1152,9 @@ struct pw_client_node *pw_client_node_new(struct pw_resource *resource, impl->fds[0] = impl->fds[1] = -1; pw_log_debug("client-node %p: new", impl); - proxy_init(&impl->proxy, NULL, core->support, core->n_support); + support = pw_core_get_support(impl->core, &n_support); + + proxy_init(&impl->proxy, NULL, support, n_support); impl->proxy.impl = impl; this->resource = resource; diff --git a/src/modules/module-client-node/protocol-native.c b/src/modules/module-client-node/protocol-native.c index 2cb34a927..f8fe40867 100644 --- a/src/modules/module-client-node/protocol-native.c +++ b/src/modules/module-client-node/protocol-native.c @@ -25,7 +25,6 @@ #include "pipewire/interfaces.h" #include "pipewire/protocol.h" #include "pipewire/client.h" -#include "pipewire/private.h" #include "extensions/protocol-native.h" #include "extensions/client-node.h" diff --git a/src/modules/module-flatpak.c b/src/modules/module-flatpak.c index 98c30328a..944f190d2 100644 --- a/src/modules/module-flatpak.c +++ b/src/modules/module-flatpak.c @@ -30,7 +30,6 @@ #include #include "pipewire/interfaces.h" -#include "pipewire/private.h" #include "pipewire/utils.h" #include "pipewire/core.h" @@ -38,6 +37,7 @@ struct impl { struct pw_core *core; + struct pw_type *type; struct pw_properties *properties; DBusConnection *bus; @@ -54,9 +54,8 @@ struct client_info { struct spa_list link; struct pw_client *client; bool is_sandboxed; - bool in_override; - const struct pw_core_proxy_methods *old_methods; - struct pw_core_proxy_methods core_methods; + struct pw_resource *core_resource; + struct pw_listener core_override; struct spa_list async_pending; struct pw_listener client_listener; }; @@ -153,15 +152,20 @@ static bool client_is_sandboxed(struct pw_client *cl) bool result; int fd; pid_t pid; + const struct ucred *ucred; - if (cl->ucred_valid) { - pw_log_info("client has trusted pid %d", cl->ucred.pid); + return true; + + ucred = pw_client_get_ucred(cl); + + if (ucred) { + pw_log_info("client has trusted pid %d", ucred->pid); } else { pw_log_info("no trusted pid found, assuming not sandboxed\n"); return false; } - pid = cl->ucred.pid; + pid = ucred->pid; sprintf(data, "/proc/%u/cgroup", pid); fd = open(data, O_RDONLY | O_CLOEXEC, 0); @@ -206,34 +210,47 @@ static bool client_is_sandboxed(struct pw_client *cl) static bool check_global_owner(struct pw_core *core, struct pw_client *client, struct pw_global *global) { + struct pw_client *owner; + const struct ucred *owner_ucred, *client_ucred; + if (global == NULL) return false; - if (global->owner == NULL) + owner = pw_global_get_owner(global); + if (owner == NULL) return true; - if (global->owner->ucred.uid == client->ucred.uid) + owner_ucred = pw_client_get_ucred(owner); + client_ucred = pw_client_get_ucred(client); + + if (owner_ucred == NULL || client_ucred == NULL) return true; - return false; + return owner_ucred->uid == client_ucred->uid; } static uint32_t do_permission(struct pw_global *global, struct pw_client *client, void *data) { - if (global->type == client->core->type.link) { - struct pw_link *link = global->object; + struct impl *impl = data; + if (pw_global_get_type(global) == impl->type->link) { + struct pw_link *link = pw_global_get_object(global); + struct pw_port *port; + struct pw_node *node; + + port = pw_link_get_output(link); + node = pw_port_get_node(port); /* we must be able to see both nodes */ - if (link->output - && !check_global_owner(client->core, client, link->output->node->global)) + if (port && node && !check_global_owner(impl->core, client, pw_node_get_global(node))) return 0; - if (link->input - && !check_global_owner(client->core, client, link->input->node->global)) + port = pw_link_get_input(link); + node = pw_port_get_node(port); + if (port && node && !check_global_owner(impl->core, client, pw_node_get_global(node))) return 0; } - else if (!check_global_owner(client->core, client, global)) + else if (!check_global_owner(impl->core, client, global)) return 0; return PW_PERM_RWX; @@ -268,16 +285,18 @@ portal_response(DBusConnection *connection, DBusMessage *msg, void *user_data) pw_log_debug("portal check result: %d", response); if (response == 0) { - cinfo->old_methods->create_node (p->resource, - p->factory_name, - p->name, - p->type, - p->version, - &p->properties->dict, - p->new_id); + pw_resource_do_parent(cinfo->core_resource, + &cinfo->core_override, + struct pw_core_proxy_methods, + create_node, + p->factory_name, + p->name, + p->type, + p->version, + &p->properties->dict, + p->new_id); } else { - pw_core_resource_error(cinfo->client->core_resource, - p->resource->id, SPA_RESULT_NO_PERMISSION, "not allowed"); + pw_resource_error(p->resource, SPA_RESULT_NO_PERMISSION, "not allowed"); } free_pending(p); @@ -289,7 +308,7 @@ portal_response(DBusConnection *connection, DBusMessage *msg, void *user_data) } -static void do_create_node(void *object, +static void do_create_node(void *data, const char *factory_name, const char *name, uint32_t type, @@ -297,10 +316,9 @@ static void do_create_node(void *object, const struct spa_dict *props, uint32_t new_id) { - struct pw_resource *resource = object; - struct client_info *cinfo = resource->access_private; + struct client_info *cinfo = data; struct impl *impl = cinfo->impl; - struct pw_client *client = resource->client; + struct pw_client *client = cinfo->client; DBusMessage *m = NULL, *r = NULL; DBusError error; pid_t pid; @@ -311,7 +329,16 @@ static void do_create_node(void *object, struct async_pending *p; if (!cinfo->is_sandboxed) { - cinfo->old_methods->create_node (object, factory_name, name, type, version, props, new_id); + pw_resource_do_parent(cinfo->core_resource, + &cinfo->core_override, + struct pw_core_proxy_methods, + create_node, + factory_name, + name, + type, + version, + props, + new_id); return; } if (strcmp(factory_name, "client-node") != 0) { @@ -320,6 +347,7 @@ static void do_create_node(void *object, } pw_log_info("ask portal for client %p", cinfo->client); + pw_client_set_busy(client, true); dbus_error_init(&error); @@ -330,7 +358,7 @@ static void do_create_node(void *object, device = "camera"; - pid = cinfo->client->ucred.pid; + pid = pw_client_get_ucred(cinfo->client)->pid; if (!dbus_message_append_args(m, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID)) goto message_failed; @@ -364,14 +392,13 @@ static void do_create_node(void *object, p->info = cinfo; p->handle = strdup(handle); p->handled = false; - p->resource = resource; + p->resource = cinfo->core_resource; p->factory_name = strdup(factory_name); p->name = strdup(name); p->type = type; p->version = version; p->properties = props ? pw_properties_new_dict(props) : NULL; p->new_id = new_id; - pw_client_set_busy(client, true); pw_log_debug("pending %p: handle %s", p, handle); spa_list_insert(cinfo->async_pending.prev, &p->link); @@ -399,13 +426,12 @@ static void do_create_node(void *object, dbus_error_free(&error); goto not_allowed; not_allowed: - pw_core_resource_error(client->core_resource, - resource->id, SPA_RESULT_NO_PERMISSION, "not allowed"); + pw_resource_error(cinfo->core_resource, SPA_RESULT_NO_PERMISSION, "not allowed"); return; } static void -do_create_link(void *object, +do_create_link(void *data, uint32_t output_node_id, uint32_t output_port_id, uint32_t input_node_id, @@ -414,48 +440,43 @@ do_create_link(void *object, const struct spa_dict *props, uint32_t new_id) { - struct pw_resource *resource = object; - struct client_info *cinfo = resource->access_private; - struct pw_client *client = resource->client; + struct client_info *cinfo = data; if (cinfo->is_sandboxed) { - pw_core_resource_error(client->core_resource, - resource->id, SPA_RESULT_NO_PERMISSION, "not allowed"); + pw_resource_error(cinfo->core_resource, 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); + pw_resource_do_parent(cinfo->core_resource, + &cinfo->core_override, + struct pw_core_proxy_methods, + create_link, + output_node_id, + output_port_id, + input_node_id, + input_port_id, + filter, + props, + new_id); } +static const struct pw_core_proxy_methods core_override = { + PW_VERSION_CORE_PROXY_METHODS, + .create_node = do_create_node, + .create_link = do_create_link, +}; + static void client_resource_impl(void *data, struct pw_resource *resource) { struct client_info *cinfo = data; - struct pw_client *client = cinfo->client; + struct impl *impl = cinfo->impl; - if (resource->type == client->core->type.core) { - struct pw_listener *impl = pw_resource_get_implementation(resource); - - if (cinfo->in_override) - return; - - cinfo->old_methods = impl->events; - cinfo->core_methods = *cinfo->old_methods; - - cinfo->in_override = true; - pw_resource_set_implementation(resource, - &cinfo->core_methods, - impl->data); - cinfo->in_override = false; - - resource->access_private = cinfo; - cinfo->core_methods.create_node = do_create_node; - cinfo->core_methods.create_link = do_create_link; + if (pw_resource_get_type(resource) == impl->type->core) { + cinfo->core_resource = resource; + pw_log_debug("module %p: add core override", impl); + pw_resource_add_override(resource, + &cinfo->core_override, + &core_override, + cinfo); } } @@ -469,8 +490,8 @@ core_global_added(void *data, struct pw_global *global) { struct impl *impl = data; - if (global->type == impl->core->type.client) { - struct pw_client *client = global->object; + if (pw_global_get_type(global) == impl->type->client) { + struct pw_client *client = pw_global_get_object(global); struct client_info *cinfo; cinfo = calloc(1, sizeof(struct client_info)); @@ -492,8 +513,8 @@ core_global_removed(void *data, struct pw_global *global) { struct impl *impl = data; - if (global->type == impl->core->type.client) { - struct pw_client *client = global->object; + if (pw_global_get_type(global) == impl->type->client) { + struct pw_client *client = pw_global_get_object(global); struct client_info *cinfo; if ((cinfo = find_client_info(impl, client))) @@ -514,14 +535,14 @@ static void dispatch_cb(struct spa_loop_utils *utils, struct spa_source *source, struct impl *impl = userdata; if (dbus_connection_dispatch(impl->bus) == DBUS_DISPATCH_COMPLETE) - pw_loop_enable_idle(impl->core->main_loop, source, false); + pw_loop_enable_idle(pw_core_get_main_loop(impl->core), source, false); } static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) { struct impl *impl = userdata; - pw_loop_enable_idle(impl->core->main_loop, + pw_loop_enable_idle(pw_core_get_main_loop(impl->core), impl->dispatch_event, status == DBUS_DISPATCH_COMPLETE ? false : true); } @@ -582,7 +603,7 @@ static dbus_bool_t add_watch(DBusWatch *watch, void *userdata) /* we dup because dbus tends to add the same fd multiple times and our epoll * implementation does not like that */ - source = pw_loop_add_io(impl->core->main_loop, + source = pw_loop_add_io(pw_core_get_main_loop(impl->core), dup(dbus_watch_get_unix_fd(watch)), dbus_to_io(watch), true, handle_io_event, watch); @@ -596,7 +617,7 @@ static void remove_watch(DBusWatch *watch, void *userdata) struct spa_source *source; if ((source = dbus_watch_get_data(watch))) - pw_loop_destroy_source(impl->core->main_loop, source); + pw_loop_destroy_source(pw_core_get_main_loop(impl->core), source); } static void toggle_watch(DBusWatch *watch, void *userdata) @@ -606,7 +627,7 @@ static void toggle_watch(DBusWatch *watch, void *userdata) source = dbus_watch_get_data(watch); - pw_loop_update_io(impl->core->main_loop, source, dbus_to_io(watch)); + pw_loop_update_io(pw_core_get_main_loop(impl->core), source, dbus_to_io(watch)); } static void @@ -636,14 +657,14 @@ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *userdata) if (!dbus_timeout_get_enabled(timeout)) return FALSE; - source = pw_loop_add_timer(impl->core->main_loop, handle_timer_event, timeout); + source = pw_loop_add_timer(pw_core_get_main_loop(impl->core), handle_timer_event, timeout); dbus_timeout_set_data(timeout, source, NULL); t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC; ts.tv_sec = t / SPA_NSEC_PER_SEC; ts.tv_nsec = t % SPA_NSEC_PER_SEC; - pw_loop_update_timer(impl->core->main_loop, source, &ts, NULL, false); + pw_loop_update_timer(pw_core_get_main_loop(impl->core), source, &ts, NULL, false); return TRUE; } @@ -653,7 +674,7 @@ static void remove_timeout(DBusTimeout *timeout, void *userdata) struct spa_source *source; if ((source = dbus_timeout_get_data(timeout))) - pw_loop_destroy_source(impl->core->main_loop, source); + pw_loop_destroy_source(pw_core_get_main_loop(impl->core), source); } static void toggle_timeout(DBusTimeout *timeout, void *userdata) @@ -672,18 +693,19 @@ static void toggle_timeout(DBusTimeout *timeout, void *userdata) } else { tsp = NULL; } - pw_loop_update_timer(impl->core->main_loop, source, tsp, NULL, false); + pw_loop_update_timer(pw_core_get_main_loop(impl->core), source, tsp, NULL, false); } static void wakeup_main(void *userdata) { struct impl *impl = userdata; - pw_loop_enable_idle(impl->core->main_loop, impl->dispatch_event, true); + pw_loop_enable_idle(pw_core_get_main_loop(impl->core), impl->dispatch_event, true); } -static struct impl *module_new(struct pw_core *core, struct pw_properties *properties) +static bool module_init(struct pw_module *module, struct pw_properties *properties) { + struct pw_core *core = pw_module_get_core(module); struct impl *impl; DBusError error; @@ -693,13 +715,14 @@ static struct impl *module_new(struct pw_core *core, struct pw_properties *prope pw_log_debug("module %p: new", impl); impl->core = core; + impl->type = pw_core_get_type(core); impl->properties = properties; impl->bus = dbus_bus_get_private(DBUS_BUS_SESSION, &error); if (impl->bus == NULL) goto error; - impl->dispatch_event = pw_loop_add_idle(core->main_loop, false, dispatch_cb, impl); + impl->dispatch_event = pw_loop_add_idle(pw_core_get_main_loop(core), false, dispatch_cb, impl); dbus_connection_set_exit_on_disconnect(impl->bus, false); dbus_connection_set_dispatch_status_function(impl->bus, dispatch_status, impl, NULL); @@ -713,15 +736,14 @@ static struct impl *module_new(struct pw_core *core, struct pw_properties *prope pw_core_add_listener(core, &impl->core_listener, &core_events, impl); - core->permission_func = do_permission; - core->permission_data = impl; + pw_core_set_permission_callback(core, do_permission, impl); - return impl; + return true; error: pw_log_error("Failed to connect to system bus: %s", error.message); dbus_error_free(&error); - return NULL; + return false; } #if 0 @@ -737,6 +759,5 @@ static void module_destroy(struct impl *impl) bool pipewire__module_init(struct pw_module *module, const char *args) { - module_new(module->core, NULL); - return true; + return module_init(module, NULL); } diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c index 338bd34b7..5674012a5 100644 --- a/src/modules/module-protocol-native.c +++ b/src/modules/module-protocol-native.c @@ -93,13 +93,14 @@ struct client_data { int fd; struct spa_source *source; struct pw_protocol_native_connection *connection; + bool busy; }; static void -process_messages(struct pw_client *client) +process_messages(struct client_data *data) { - struct client_data *data = client->user_data; struct pw_protocol_native_connection *conn = data->connection; + struct pw_client *client = data->client; uint8_t opcode; uint32_t id; uint32_t size; @@ -108,31 +109,40 @@ process_messages(struct pw_client *client) while (pw_protocol_native_connection_get_next(conn, &opcode, &id, &message, &size)) { struct pw_resource *resource; const struct pw_protocol_native_demarshal *demarshal; + const struct pw_protocol_marshal *marshal; + uint32_t permissions; + + /* when the client is busy processing an async action, stop processing messages + * for the client until it finishes the action */ + if (data->busy) + break; pw_log_trace("protocol-native %p: got message %d from %u", client->protocol, opcode, id); - resource = pw_map_lookup(&client->objects, id); + resource = pw_client_get_resource(client, id); if (resource == NULL) { pw_log_error("protocol-native %p: unknown resource %u", client->protocol, id); continue; } - if ((resource->permissions & PW_PERM_X) == 0) { + permissions = pw_resource_get_permissions(resource); + if ((permissions & PW_PERM_X) == 0) { pw_log_error("protocol-native %p: execute not allowed on resource %u", client->protocol, id); continue; } - if (opcode >= resource->marshal->n_methods) + marshal = pw_resource_get_marshal(resource); + if (marshal == NULL || opcode >= marshal->n_methods) goto invalid_method; - demarshal = resource->marshal->method_demarshal; + demarshal = marshal->method_demarshal; if (!demarshal[opcode].func) goto invalid_message; if ((demarshal[opcode].flags & PW_PROTOCOL_NATIVE_PERM_W) && - ((resource->permissions & PW_PERM_X) == 0)) { + ((permissions & PW_PERM_X) == 0)) { pw_log_error("protocol-native %p: method %u requires write access on %u", client->protocol, opcode, id); continue; @@ -144,11 +154,6 @@ process_messages(struct pw_client *client) if (!demarshal[opcode].func (resource, message, size)) goto invalid_message; - - /* when the client is busy processing an async action, stop processing messages - * for the client until it finishes the action */ - if (client->busy) - break; } return; @@ -167,17 +172,20 @@ process_messages(struct pw_client *client) static void client_busy_changed(void *data, bool busy) { - struct pw_client *client = data; - struct client_data *c = client->user_data; + struct client_data *c = data; + struct pw_client *client = c->client; enum spa_io mask = SPA_IO_ERR | SPA_IO_HUP; + c->busy = busy; + if (!busy) mask |= SPA_IO_IN; + pw_log_debug("protocol-native %p: busy changed %d", client->protocol, busy); pw_loop_update_io(client->core->main_loop, c->source, mask); - if (busy) - process_messages(client); + if (!busy) + process_messages(c); } @@ -198,7 +206,8 @@ static void connection_data(struct spa_loop_utils *utils, struct spa_source *source, int fd, enum spa_io mask, void *data) { - struct pw_client *client = data; + struct client_data *this = data; + struct pw_client *client = this->client; if (mask & (SPA_IO_ERR | SPA_IO_HUP)) { pw_log_error("protocol-native %p: got connection error", client->protocol); @@ -207,13 +216,13 @@ connection_data(struct spa_loop_utils *utils, } if (mask & SPA_IO_IN) - process_messages(client); + process_messages(this); } static void client_free(void *data) { - struct pw_client *client = data; - struct client_data *this = client->user_data; + struct client_data *this = data; + struct pw_client *client = this->client; pw_loop_destroy_source(client->protocol->core->main_loop, this->source); spa_list_remove(&client->protocol_link); @@ -236,6 +245,7 @@ static struct pw_client *client_new(struct listener *l, int fd) struct protocol_data *pd = protocol->user_data; socklen_t len; struct ucred ucred, *ucredp; + struct pw_core *core = protocol->core; len = sizeof(ucred); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { @@ -245,16 +255,20 @@ static struct pw_client *client_new(struct listener *l, int fd) ucredp = &ucred; } - client = pw_client_new(protocol->core, pd->module->global, ucredp, NULL, sizeof(struct client_data)); + client = pw_client_new(protocol->core, + pw_module_get_global(pd->module), + ucredp, + NULL, + sizeof(struct client_data)); if (client == NULL) goto no_client; this = pw_client_get_user_data(client); this->client = client; this->fd = fd; - this->source = pw_loop_add_io(protocol->core->main_loop, + this->source = pw_loop_add_io(pw_core_get_main_loop(core), this->fd, - SPA_IO_ERR | SPA_IO_HUP, false, connection_data, client); + SPA_IO_ERR | SPA_IO_HUP, false, connection_data, this); if (this->source == NULL) goto no_source; @@ -265,14 +279,14 @@ static struct pw_client *client_new(struct listener *l, int fd) client->protocol = protocol; spa_list_insert(l->this.client_list.prev, &client->protocol_link); - pw_client_add_listener(client, &this->client_listener, &client_events, client); + pw_client_add_listener(client, &this->client_listener, &client_events, this); - pw_global_bind(protocol->core->global, client, PW_PERM_RWX, PW_VERSION_CORE, 0); + pw_global_bind(pw_core_get_global(core), client, PW_PERM_RWX, PW_VERSION_CORE, 0); return client; no_connection: - pw_loop_destroy_source(protocol->core->main_loop, this->source); + pw_loop_destroy_source(pw_core_get_main_loop(core), this->source); no_source: pw_client_destroy(client); no_client: @@ -389,7 +403,7 @@ static bool add_socket(struct pw_protocol *protocol, struct listener *l) return false; } - l->loop = protocol->core->main_loop; + l->loop = pw_core_get_main_loop(protocol->core); l->source = pw_loop_add_io(l->loop, l->fd, SPA_IO_IN, false, socket_data, l); if (l->source == NULL) return false; @@ -398,7 +412,7 @@ static bool add_socket(struct pw_protocol *protocol, struct listener *l) } static const char * -get_name(struct pw_properties *properties) +get_name(const struct pw_properties *properties) { const char *name = NULL; @@ -459,10 +473,11 @@ on_remote_data(struct spa_loop_utils *utils, struct connection *impl = data; struct pw_remote *this = impl->this.remote; struct pw_protocol_native_connection *conn = impl->connection; + struct pw_core *core = pw_remote_get_core(this); if (mask & (SPA_IO_ERR | SPA_IO_HUP)) { pw_log_error("protocol-native %p: got connection error", impl); - pw_loop_destroy_source(this->core->main_loop, impl->source); + pw_loop_destroy_source(pw_core_get_main_loop(core), impl->source); impl->source = NULL; pw_remote_update_state(this, PW_REMOTE_STATE_ERROR, "connection error"); return; @@ -478,21 +493,25 @@ on_remote_data(struct spa_loop_utils *utils, && pw_protocol_native_connection_get_next(conn, &opcode, &id, &message, &size)) { struct pw_proxy *proxy; const struct pw_protocol_native_demarshal *demarshal; + const struct pw_protocol_marshal *marshal; pw_log_trace("protocol-native %p: got message %d from %u", this, opcode, id); - proxy = pw_map_lookup(&this->objects, id); - if (proxy == NULL || proxy->marshal == NULL) { + proxy = pw_remote_get_proxy(this, id); + + if (proxy == NULL) { pw_log_error("protocol-native %p: could not find proxy %u", this, id); continue; } - if (opcode >= proxy->marshal->n_events) { + + marshal = pw_proxy_get_marshal(proxy); + if (marshal == NULL || opcode >= marshal->n_events) { pw_log_error("protocol-native %p: invalid method %u for %u", this, opcode, id); continue; } - demarshal = proxy->marshal->event_demarshal; + demarshal = marshal->event_demarshal; if (!demarshal[opcode].func) { pw_log_error("protocol-native %p: function %d not implemented on %u", this, opcode, id); @@ -665,7 +684,7 @@ impl_add_listener(struct pw_protocol *protocol, spa_list_init(&this->client_list); this->destroy = destroy_listener; - name = get_name(core->properties); + name = get_name(pw_core_get_properties(core)); if (!init_socket_name(l, name)) goto error; @@ -679,7 +698,7 @@ impl_add_listener(struct pw_protocol *protocol, spa_list_insert(protocol->listener_list.prev, &this->link); l->hooks.before = on_before_hook; - pw_loop_add_hooks(protocol->core->main_loop, &l->hooks); + pw_loop_add_hooks(pw_core_get_main_loop(core), &l->hooks); pw_log_info("protocol-native %p: Added listener %p", protocol, this); @@ -761,7 +780,7 @@ const static struct pw_protocol_native_ext protocol_ext_impl = { static bool module_init(struct pw_module *module, struct pw_properties *properties) { - struct pw_core *core = module->core; + struct pw_core *core = pw_module_get_core(module); struct pw_protocol *this; const char *val; struct protocol_data *d; @@ -783,7 +802,7 @@ static bool module_init(struct pw_module *module, struct pw_properties *properti d = pw_protocol_get_user_data(this); d->module = module; - if ((val = pw_properties_get(core->properties, "pipewire.daemon"))) { + if ((val = pw_properties_get(pw_core_get_properties(core), "pipewire.daemon"))) { if (atoi(val) == 1) impl_add_listener(this, core, properties); } diff --git a/src/modules/spa/spa-monitor.c b/src/modules/spa/spa-monitor.c index aa7b18377..14ee6b527 100644 --- a/src/modules/spa/spa-monitor.c +++ b/src/modules/spa/spa-monitor.c @@ -189,13 +189,13 @@ static void update_monitor(struct pw_core *core, const char *name) { const char *monitors; struct spa_dict_item item; - const struct spa_dict *props; + const struct pw_properties *props; struct spa_dict dict = SPA_DICT_INIT(1, &item); props = pw_core_get_properties(core); if (props) - monitors = spa_dict_lookup(props, "monitors"); + monitors = pw_properties_get(props, "monitors"); else monitors = NULL; diff --git a/src/pipewire/client.c b/src/pipewire/client.c index 3a6e7ba5d..1ddf1d612 100644 --- a/src/pipewire/client.c +++ b/src/pipewire/client.c @@ -76,8 +76,7 @@ client_bind_func(struct pw_global *global, no_mem: pw_log_error("can't create client resource"); - pw_core_resource_error(client->core_resource, - client->core_resource->id, SPA_RESULT_NO_MEMORY, "no memory"); + pw_resource_error(client->core_resource, SPA_RESULT_NO_MEMORY, "no memory"); return SPA_RESULT_NO_MEMORY; } @@ -135,6 +134,16 @@ struct pw_core *pw_client_get_core(struct pw_client *client) return client->core; } +struct pw_resource *pw_client_get_core_resource(struct pw_client *client) +{ + return client->core_resource; +} + +struct pw_resource *pw_client_get_resource(struct pw_client *client, uint32_t id) +{ + return pw_map_lookup(&client->objects, id); +} + struct pw_global *pw_client_get_global(struct pw_client *client) { return client->global; @@ -250,6 +259,7 @@ void pw_client_update_properties(struct pw_client *client, const struct spa_dict void pw_client_set_busy(struct pw_client *client, bool busy) { if (client->busy != busy) { + pw_log_debug("client %p: busy %d", client, busy); client->busy = busy; pw_listener_list_emit(&client->listener_list, struct pw_client_events, busy_changed, busy); } diff --git a/src/pipewire/client.h b/src/pipewire/client.h index 5f6929d7a..aa9b205be 100644 --- a/src/pipewire/client.h +++ b/src/pipewire/client.h @@ -81,25 +81,37 @@ struct pw_client; * See also \ref page_resource */ +/** The events that a client can emit */ struct pw_client_events { #define PW_VERSION_CLIENT_EVENTS 0 uint32_t version; + /** emited when the client is destroyed */ void (*destroy) (void *data); + /** emited right before the client is freed */ void (*free) (void *data); + /** emited when the client info changed */ void (*info_changed) (void *data, struct pw_client_info *info); + /** emited when a new resource is added for client */ void (*resource_added) (void *data, struct pw_resource *resource); + /** emited when an implementation is set on a resource. This can + * be used to override the implementation */ void (*resource_impl) (void *data, struct pw_resource *resource); + /** emited when a resource is removed */ void (*resource_removed) (void *data, struct pw_resource *resource); + /** emited when the client becomes busy processing an asynchronous + * message. In the busy state no messages should be processed. + * Processing should resume when the client becomes not busy */ void (*busy_changed) (void *data, bool busy); }; +/** Create a new client. This is mainly used by protocols. */ struct pw_client * pw_client_new(struct pw_core *core, struct pw_global *parent, @@ -109,12 +121,20 @@ pw_client_new(struct pw_core *core, void pw_client_destroy(struct pw_client *client); -struct pw_core *pw_client_get_core(struct pw_client *client); +const struct pw_client_info *pw_client_get_info(struct pw_client *client); -struct pw_global *pw_client_get_global(struct pw_client *client); +void pw_client_update_properties(struct pw_client *client, const struct spa_dict *dict); const struct pw_properties *pw_client_get_properties(struct pw_client *client); +struct pw_core *pw_client_get_core(struct pw_client *client); + +struct pw_resource *pw_client_get_core_resource(struct pw_client *client); + +struct pw_resource *pw_client_get_resource(struct pw_client *client, uint32_t id); + +struct pw_global *pw_client_get_global(struct pw_client *client); + const struct ucred *pw_client_get_ucred(struct pw_client *client); void *pw_client_get_user_data(struct pw_client *client); @@ -125,10 +145,6 @@ void pw_client_add_listener(struct pw_client *client, void *data); -const struct pw_client_info *pw_client_get_info(struct pw_client *client); - -void pw_client_update_properties(struct pw_client *client, const struct spa_dict *dict); - /** Mark the client busy. This can be used when an asynchronous operation is * started and no further processing is allowed to happen for the client */ void pw_client_set_busy(struct pw_client *client, bool busy); diff --git a/src/pipewire/core.c b/src/pipewire/core.c index 7eca014cd..532eb2e43 100644 --- a/src/pipewire/core.c +++ b/src/pipewire/core.c @@ -500,14 +500,30 @@ pw_core_add_global(struct pw_core *core, return this; } +const struct pw_core_info *pw_core_get_info(struct pw_core *core) +{ + return &core->info; +} + +struct pw_global *pw_core_get_global(struct pw_core *core) +{ + return core->global; +} + struct pw_core *pw_global_get_core(struct pw_global *global) { return global->core; } -const struct pw_core_info *pw_core_get_info(struct pw_core *core) + +struct pw_client *pw_global_get_owner(struct pw_global *global) { - return &core->info; + return global->owner; +} + +struct pw_global *pw_global_get_parent(struct pw_global *global) +{ + return global->parent; } uint32_t pw_global_get_type(struct pw_global *global) @@ -610,6 +626,14 @@ void pw_core_add_listener(struct pw_core *core, pw_listener_list_add(&core->listener_list, listener, events, data); } +void pw_core_set_permission_callback(struct pw_core *core, + pw_permission_func_t callback, + void *data) +{ + core->permission_func = callback; + core->permission_data = data; +} + struct pw_type *pw_core_get_type(struct pw_core *core) { return &core->type; @@ -626,9 +650,9 @@ struct pw_loop *pw_core_get_main_loop(struct pw_core *core) return core->main_loop; } -const struct spa_dict *pw_core_get_properties(struct pw_core *core) +const struct pw_properties *pw_core_get_properties(struct pw_core *core) { - return &core->properties->dict; + return core->properties; } /** Update core properties diff --git a/src/pipewire/core.h b/src/pipewire/core.h index eea6b2bb0..f339256a0 100644 --- a/src/pipewire/core.h +++ b/src/pipewire/core.h @@ -162,11 +162,17 @@ void pw_core_add_listener(struct pw_core *core, const struct pw_core_events *events, void *data); +void pw_core_set_permission_callback(struct pw_core *core, + pw_permission_func_t callback, + void *data); + struct pw_type *pw_core_get_type(struct pw_core *core); const struct pw_core_info *pw_core_get_info(struct pw_core *core); -const struct spa_dict *pw_core_get_properties(struct pw_core *core); +struct pw_global *pw_core_get_global(struct pw_core *core); + +const struct pw_properties *pw_core_get_properties(struct pw_core *core); const struct spa_support *pw_core_get_support(struct pw_core *core, uint32_t *n_support); diff --git a/src/pipewire/listener.h b/src/pipewire/listener.h index 110a1bd97..c402a6111 100644 --- a/src/pipewire/listener.h +++ b/src/pipewire/listener.h @@ -57,16 +57,26 @@ static inline void pw_listener_remove(struct pw_listener *listener) spa_list_remove(&listener->link); } -#define pw_listener_list_emit(l,type,method,...) ({ \ - struct pw_listener_list *list = l; \ - struct pw_listener *ci, *t; \ - spa_list_for_each_safe(ci, t, &list->list, link) { \ - const type *cb = ci->events; \ - if (cb->method) \ - cb->method(ci->data, ## __VA_ARGS__); \ - } \ +#define pw_listener_list_do_emit(l,start,type,method,once,...) ({ \ + struct pw_listener_list *list = l; \ + struct spa_list *s = start ? (struct spa_list *)start : &list->list; \ + struct pw_listener *ci, *t; \ + spa_list_for_each_safe_next(ci, t, &list->list, s, link) { \ + const type *cb = ci->events; \ + if (cb->method) { \ + cb->method(ci->data, ## __VA_ARGS__); \ + if (once) \ + break; \ + } \ + } \ }); +#define pw_listener_list_emit(l,t,m,...) pw_listener_list_do_emit(l,NULL,t,m,false,##__VA_ARGS__) +#define pw_listener_list_emit_once(l,t,m,...) pw_listener_list_do_emit(l,NULL,t,m,true,##__VA_ARGS__) + +#define pw_listener_list_emit_start(l,s,t,m,...) pw_listener_list_do_emit(l,s,t,m,false,##__VA_ARGS__) +#define pw_listener_list_emit_once_start(l,s,t,m,...) pw_listener_list_do_emit(l,s,t,m,true,##__VA_ARGS__) + #ifdef __cplusplus } #endif diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 39e7fbbdb..e6cbe9c1a 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -270,7 +270,7 @@ struct pw_resource { uint32_t version; /**< version of the client interface */ struct pw_listener implementation; - + struct pw_listener_list implementation_list; struct pw_listener_list listener_list; const struct pw_protocol_marshal *marshal; diff --git a/src/pipewire/proxy.c b/src/pipewire/proxy.c index ba1c22ca8..3998f2788 100644 --- a/src/pipewire/proxy.c +++ b/src/pipewire/proxy.c @@ -132,7 +132,7 @@ struct pw_listener_list *pw_proxy_get_proxy_listeners(struct pw_proxy *proxy) return &proxy->proxy_listener_list; } -const void *pw_proxy_get_proxy_implementation(struct pw_proxy *proxy) +const struct pw_protocol_marshal *pw_proxy_get_marshal(struct pw_proxy *proxy) { - return proxy->marshal->method_marshal; + return proxy->marshal; } diff --git a/src/pipewire/proxy.h b/src/pipewire/proxy.h index 9e4aeadf7..f31a84aff 100644 --- a/src/pipewire/proxy.h +++ b/src/pipewire/proxy.h @@ -86,9 +86,10 @@ struct pw_remote; */ struct pw_proxy; -#include #include -#include +#include +#include +#include struct pw_proxy_events { #define PW_VERSION_PROXY_EVENTS 0 @@ -125,10 +126,10 @@ struct pw_protocol *pw_proxy_get_protocol(struct pw_proxy *proxy); struct pw_listener_list *pw_proxy_get_proxy_listeners(struct pw_proxy *proxy); -const void *pw_proxy_get_proxy_implementation(struct pw_proxy *proxy); +const struct pw_protocol_marshal *pw_proxy_get_marshal(struct pw_proxy *proxy); #define pw_proxy_notify(p,type,event,...) pw_listener_list_emit(pw_proxy_get_proxy_listeners(p),type,event,## __VA_ARGS__) -#define pw_proxy_do(p,type,method,...) ((type*) pw_proxy_get_proxy_implementation(p))->method(p, ## __VA_ARGS__) +#define pw_proxy_do(p,type,method,...) ((type*) pw_proxy_get_marshal(p)->method_marshal)->method(p, ## __VA_ARGS__) #ifdef __cplusplus } diff --git a/src/pipewire/remote.c b/src/pipewire/remote.c index e4877d56c..62cd7f306 100644 --- a/src/pipewire/remote.c +++ b/src/pipewire/remote.c @@ -337,6 +337,11 @@ const struct pw_core_info *pw_remote_get_core_info(struct pw_remote *remote) return remote->info; } +struct pw_proxy *pw_remote_get_proxy(struct pw_remote *remote, uint32_t id) +{ + return pw_map_lookup(&remote->objects, id); +} + int pw_remote_connect(struct pw_remote *remote) { int res; diff --git a/src/pipewire/remote.h b/src/pipewire/remote.h index cd85bbe9a..bb61f7b41 100644 --- a/src/pipewire/remote.h +++ b/src/pipewire/remote.h @@ -168,6 +168,8 @@ struct pw_core_proxy * pw_remote_get_core_proxy(struct pw_remote *remote); /** Get the remote core info, can only be called when connected */ const struct pw_core_info *pw_remote_get_core_info(struct pw_remote *remote); +struct pw_proxy *pw_remote_get_proxy(struct pw_remote *remote, uint32_t id); + /** Disconnect from the remote PipeWire. \memberof pw_remote */ void pw_remote_disconnect(struct pw_remote *remote); diff --git a/src/pipewire/resource.c b/src/pipewire/resource.c index a2a715617..bb556cac0 100644 --- a/src/pipewire/resource.c +++ b/src/pipewire/resource.c @@ -51,6 +51,7 @@ struct pw_resource *pw_resource_new(struct pw_client *client, this->type = type; this->version = version; + pw_listener_list_init(&this->implementation_list); pw_listener_list_init(&this->listener_list); if (id == SPA_ID_INVALID) { @@ -64,6 +65,7 @@ struct pw_resource *pw_resource_new(struct pw_client *client, this->user_data = SPA_MEMBER(impl, sizeof(struct impl), void); this->marshal = pw_protocol_get_marshal(client->protocol, type); + pw_listener_list_add(&this->implementation_list, &this->implementation, NULL, NULL); pw_log_debug("resource %p: new for client %p id %u", this, client, id); pw_listener_list_emit(&client->listener_list, struct pw_client_events, resource_added, this); @@ -91,6 +93,11 @@ uint32_t pw_resource_get_permissions(struct pw_resource *resource) return resource->permissions; } +uint32_t pw_resource_get_type(struct pw_resource *resource) +{ + return resource->type; +} + struct pw_protocol *pw_resource_get_protocol(struct pw_resource *resource) { return resource->client->protocol; @@ -121,14 +128,24 @@ void pw_resource_set_implementation(struct pw_resource *resource, pw_listener_list_emit(&client->listener_list, struct pw_client_events, resource_impl, resource); } -struct pw_listener *pw_resource_get_implementation(struct pw_resource *resource) +void pw_resource_add_override(struct pw_resource *resource, + struct pw_listener *listener, + const void *implementation, + void *data) { - return &resource->implementation; + listener->events = implementation; + listener->data = data; + spa_list_insert(&resource->implementation_list.list, &listener->link); } -const void *pw_resource_get_proxy_notify(struct pw_resource *resource) +struct pw_listener_list *pw_resource_get_implementation(struct pw_resource *resource) { - return resource->marshal->event_marshal; + return &resource->implementation_list; +} + +const struct pw_protocol_marshal *pw_resource_get_marshal(struct pw_resource *resource) +{ + return resource->marshal; } void pw_resource_error(struct pw_resource *resource, int result, const char *error) diff --git a/src/pipewire/resource.h b/src/pipewire/resource.h index 50646cbf5..b6aa74e38 100644 --- a/src/pipewire/resource.h +++ b/src/pipewire/resource.h @@ -84,6 +84,8 @@ uint32_t pw_resource_get_id(struct pw_resource *resource); uint32_t pw_resource_get_permissions(struct pw_resource *resource); +uint32_t pw_resource_get_type(struct pw_resource *resource); + struct pw_protocol *pw_resource_get_protocol(struct pw_resource *resource); void *pw_resource_get_user_data(struct pw_resource *resource); @@ -97,20 +99,22 @@ void pw_resource_set_implementation(struct pw_resource *resource, const void *implementation, void *data); +void pw_resource_add_override(struct pw_resource *resource, + struct pw_listener *listener, + const void *implementation, + void *data); + void pw_resource_error(struct pw_resource *resource, int result, const char *error); -struct pw_listener *pw_resource_get_implementation(struct pw_resource *resource); +struct pw_listener_list *pw_resource_get_implementation(struct pw_resource *resource); -const void *pw_resource_get_proxy_notify(struct pw_resource *resource); +const struct pw_protocol_marshal *pw_resource_get_marshal(struct pw_resource *resource); -#define pw_resource_do(r,type,method,...) ({ \ - struct pw_listener *l = pw_resource_get_implementation(r); \ - const type *cb = l->events; \ - if (cb->method) \ - cb->method(l->data, ## __VA_ARGS__); \ -}); +#define pw_resource_do(r,type,method,...) pw_listener_list_emit_once(pw_resource_get_implementation(r),type,method,## __VA_ARGS__) -#define pw_resource_notify(r,type,event,...) ((type*) pw_resource_get_proxy_notify(r))->event(r, ## __VA_ARGS__) +#define pw_resource_do_parent(r,l,type,method,...) pw_listener_list_emit_once_start(pw_resource_get_implementation(r),l,type,method,## __VA_ARGS__) + +#define pw_resource_notify(r,type,event,...) ((type*) pw_resource_get_marshal(r)->event_marshal)->event(r, ## __VA_ARGS__) #ifdef __cplusplus }