From e026f55c97df0c42b14bb522dd217ed9479ba5bc Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Sat, 27 Oct 2018 17:31:03 +0100 Subject: [PATCH] protocol: add more methods on client Add method to get and set permissions on a client Add method to send error to client. --- .../module-protocol-native/protocol-native.c | 168 ++++++++++++++++- src/pipewire/client.c | 175 ++++++++++++++---- src/pipewire/client.h | 2 + src/pipewire/interfaces.h | 63 ++++++- src/pipewire/link.c | 5 +- src/tools/pipewire-cli.c | 43 ++++- 6 files changed, 410 insertions(+), 46 deletions(-) diff --git a/src/modules/module-protocol-native/protocol-native.c b/src/modules/module-protocol-native/protocol-native.c index a1261e4bf..6be0b121a 100644 --- a/src/modules/module-protocol-native/protocol-native.c +++ b/src/modules/module-protocol-native/protocol-native.c @@ -1017,6 +1017,155 @@ static int client_demarshal_info(void *object, void *data, size_t size) return 0; } +static void client_marshal_permissions(void *object, const struct spa_dict *dict) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + uint32_t i, n_items; + + b = pw_protocol_native_begin_resource(resource, PW_CLIENT_PROXY_EVENT_PERMISSIONS); + + n_items = dict ? dict->n_items : 0; + + spa_pod_builder_add(b, + "[", + "i", n_items, NULL); + + for (i = 0; i < n_items; i++) { + spa_pod_builder_add(b, + "s", dict->items[i].key, + "s", dict->items[i].value, NULL); + } + spa_pod_builder_add(b, "]", NULL); + + pw_protocol_native_end_resource(resource, b); +} + +static int client_demarshal_permissions(void *object, void *data, size_t size) +{ + struct pw_proxy *proxy = object; + struct spa_dict props; + struct spa_pod_parser prs; + uint32_t i; + + spa_pod_parser_init(&prs, data, size, 0); + if (spa_pod_parser_get(&prs, "[ i", &props.n_items, NULL) < 0) + return -EINVAL; + + props.items = alloca(props.n_items * sizeof(struct spa_dict_item)); + for (i = 0; i < props.n_items; i++) { + if (spa_pod_parser_get(&prs, + "s", &props.items[i].key, + "s", &props.items[i].value, + NULL) < 0) + return -EINVAL; + } + pw_proxy_notify(proxy, struct pw_client_proxy_events, permissions, 0, &props); + return 0; +} + +static void client_marshal_error(void *object, uint32_t id, int res, const char *error) +{ + struct pw_proxy *proxy = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_proxy(proxy, PW_CLIENT_PROXY_METHOD_ERROR); + spa_pod_builder_add_struct(b, + "i", id, + "i", res, + "s", error); + pw_protocol_native_end_proxy(proxy, b); +} + +static int client_demarshal_error(void *object, void *data, size_t size) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + uint32_t id, res; + const char *error; + + spa_pod_parser_init(&prs, data, size, 0); + if (spa_pod_parser_get(&prs, + "[ i", &id, + "i", &res, + "s", &error, NULL) < 0) + return -EINVAL; + + pw_resource_do(resource, struct pw_client_proxy_methods, error, 0, id, res, error); + return 0; +} + +static void client_marshal_get_permissions(void *object) +{ + struct pw_proxy *proxy = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_proxy(proxy, PW_CLIENT_PROXY_METHOD_GET_PERMISSIONS); + + spa_pod_builder_add_struct(b, "P", NULL); + + pw_protocol_native_end_proxy(proxy, b); +} + +static int client_demarshal_get_permissions(void *object, void *data, size_t size) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + void *ptr; + + spa_pod_parser_init(&prs, data, size, 0); + if (spa_pod_parser_get(&prs, "[P]", &ptr, NULL) < 0) + return -EINVAL; + + pw_resource_do(resource, struct pw_client_proxy_methods, get_permissions, 0); + return 0; +} + +static void client_marshal_update_permissions(void *object, const struct spa_dict *props) +{ + struct pw_proxy *proxy = object; + struct spa_pod_builder *b; + int i, n_items; + + b = pw_protocol_native_begin_proxy(proxy, PW_CLIENT_PROXY_METHOD_UPDATE_PERMISSIONS); + + n_items = props ? props->n_items : 0; + + spa_pod_builder_add(b, "[ i", n_items, NULL); + + for (i = 0; i < n_items; i++) { + spa_pod_builder_add(b, + "s", props->items[i].key, + "s", props->items[i].value, NULL); + } + spa_pod_builder_add(b, "]", NULL); + + pw_protocol_native_end_proxy(proxy, b); +} + +static int client_demarshal_update_permissions(void *object, void *data, size_t size) +{ + struct pw_resource *resource = object; + struct spa_dict props; + struct spa_pod_parser prs; + uint32_t i; + + spa_pod_parser_init(&prs, data, size, 0); + if (spa_pod_parser_get(&prs, "[ i", &props.n_items, NULL) < 0) + return -EINVAL; + + props.items = alloca(props.n_items * sizeof(struct spa_dict_item)); + for (i = 0; i < props.n_items; i++) { + if (spa_pod_parser_get(&prs, + "s", &props.items[i].key, + "s", &props.items[i].value, + NULL) < 0) + return -EINVAL; + } + pw_resource_do(resource, struct pw_client_proxy_methods, update_permissions, 0, &props); + return 0; +} + static void link_marshal_info(void *object, struct pw_link_info *info) { struct pw_resource *resource = object; @@ -1342,19 +1491,36 @@ static const struct pw_protocol_marshal pw_protocol_native_port_marshal = { PW_PORT_PROXY_EVENT_NUM, }; +static const struct pw_client_proxy_methods pw_protocol_native_client_method_marshal = { + PW_VERSION_CLIENT_PROXY_METHODS, + &client_marshal_error, + &client_marshal_get_permissions, + &client_marshal_update_permissions, +}; + +static const struct pw_protocol_native_demarshal pw_protocol_native_client_method_demarshal[] = { + { &client_demarshal_error, PW_PROTOCOL_NATIVE_PERM_W, }, + { &client_demarshal_get_permissions, 0, }, + { &client_demarshal_update_permissions, PW_PROTOCOL_NATIVE_PERM_W, }, +}; + static const struct pw_client_proxy_events pw_protocol_native_client_event_marshal = { PW_VERSION_CLIENT_PROXY_EVENTS, &client_marshal_info, + &client_marshal_permissions, }; static const struct pw_protocol_native_demarshal pw_protocol_native_client_event_demarshal[] = { { &client_demarshal_info, 0, }, + { &client_demarshal_permissions, 0, } }; static const struct pw_protocol_marshal pw_protocol_native_client_marshal = { PW_TYPE_INTERFACE_Client, PW_VERSION_CLIENT, - NULL, NULL, 0, + &pw_protocol_native_client_method_marshal, + pw_protocol_native_client_method_demarshal, + PW_CLIENT_PROXY_METHOD_NUM, &pw_protocol_native_client_event_marshal, pw_protocol_native_client_event_demarshal, PW_CLIENT_PROXY_EVENT_NUM, diff --git a/src/pipewire/client.c b/src/pipewire/client.c index 7ed71c7b5..9525ef28f 100644 --- a/src/pipewire/client.c +++ b/src/pipewire/client.c @@ -47,21 +47,42 @@ struct resource_data { /** find a specific permission for a global or NULL when there is none */ static struct permission * -find_permission(struct pw_client *client, struct pw_global *global) +find_permission(struct pw_client *client, uint32_t id) { struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this); struct permission *p; - if (!pw_array_check_index(&impl->permissions, global->id, struct permission)) + if (!pw_array_check_index(&impl->permissions, id, struct permission)) return NULL; - p = pw_array_get_unchecked(&impl->permissions, global->id, struct permission); + p = pw_array_get_unchecked(&impl->permissions, id, struct permission); if (p->permissions == -1) return NULL; else return p; } +static struct permission *ensure_permissions(struct pw_client *client, uint32_t id) +{ + struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this); + struct permission *p; + size_t len, i; + + len = pw_array_get_len(&impl->permissions, struct permission); + if (len <= id) { + size_t diff = id - len + 1; + + p = pw_array_add(&impl->permissions, diff * sizeof(struct permission)); + if (p == NULL) + return NULL; + + for (i = 0; i < diff; i++) + p[i].permissions = -1; + } + p = pw_array_get_unchecked(&impl->permissions, id, struct permission); + return p; +} + /** \endcond */ static uint32_t @@ -71,13 +92,111 @@ client_permission_func(struct pw_global *global, struct impl *impl = data; struct permission *p; - p = find_permission(client, global); + p = find_permission(client, global->id); if (p == NULL) return impl->permissions_default; else return p->permissions; } +static uint32_t parse_mask(const char *str) +{ + uint32_t mask = 0; + + while (*str != '\0') { + switch (*str++) { + case 'r': + mask |= PW_PERM_R; + break; + case 'w': + mask |= PW_PERM_W; + break; + case 'x': + mask |= PW_PERM_X; + break; + } + } + return mask; +} + +static void client_error(void *object, uint32_t id, int res, const char *error) +{ + struct pw_resource *resource = object; + struct resource_data *data = pw_resource_get_user_data(resource); + struct pw_client *client = data->client; + pw_resource_error(client->core_resource, id, res, error); +} + +static void client_get_permissions(void *object) +{ + struct pw_resource *resource = object; + struct resource_data *data = pw_resource_get_user_data(resource); + struct pw_client *client = data->client; +} + +static void client_update_permissions(void *object, const struct spa_dict *props) +{ + struct pw_resource *resource = object; + struct resource_data *data = pw_resource_get_user_data(resource); + struct pw_client *client = data->client; + struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this); + const char *str; + int i, len; + + for (i = 0; i < props->n_items; i++) { + str = props->items[i].value; + + pw_log_debug("client %p: %s %s", client, props->items[i].key, str); + + if (strcmp(props->items[i].key, PW_CORE_PROXY_PERMISSIONS_DEFAULT) == 0) { + impl->permissions_default = parse_mask(str); + pw_log_debug("client %p: set default permissions to %08x", + client, impl->permissions_default); + } + else if (strcmp(props->items[i].key, PW_CORE_PROXY_PERMISSIONS_GLOBAL) == 0) { + struct pw_global *global; + uint32_t global_id, old_perm, new_perm; + struct permission *p; + + /* permissions.update=:[r][w][x] */ + len = strcspn(str, ":"); + if (len == 0) + continue; + + global_id = atoi(str); + global = pw_core_find_global(client->core, global_id); + if (global == NULL) { + pw_log_warn("client %p: invalid global %d", client, global_id); + continue; + } + + p = ensure_permissions(client, global_id); + old_perm = p->permissions == -1 ? impl->permissions_default : p->permissions; + new_perm = parse_mask(str + len); + + pw_log_debug("client %p: %08x %08x", client, old_perm, new_perm); + + p->permissions = new_perm; + + if (PW_PERM_IS_R(old_perm) && !PW_PERM_IS_R(new_perm)) { + pw_global_revoke(global, client); + } + else if (!PW_PERM_IS_R(old_perm) && PW_PERM_IS_R(new_perm)) { + pw_global_grant(global, client); + } + } + } + if (impl->permissions_default != 0) + pw_client_set_busy(client, false); +} + +static const struct pw_client_proxy_methods client_methods = { + PW_VERSION_CLIENT_PROXY_METHODS, + .error = client_error, + .get_permissions = client_get_permissions, + .update_permissions = client_update_permissions +}; + static void client_unbind_func(void *data) { struct pw_resource *resource = data; @@ -89,7 +208,6 @@ static const struct pw_resource_events resource_events = { .destroy = client_unbind_func, }; - static void global_bind(void *_data, struct pw_client *client, uint32_t permissions, uint32_t version, uint32_t id) @@ -131,7 +249,7 @@ core_global_removed(void *data, struct pw_global *global) struct pw_client *client = &impl->this; struct permission *p; - p = find_permission(client, global); + p = find_permission(client, global->id); pw_log_debug("client %p: global %d removed, %p", client, global->id, p); if (p != NULL) p->permissions = -1; @@ -385,21 +503,10 @@ static int do_permissions(void *data, struct pw_global *global) struct pw_client *client = update->client; struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this); struct permission *p; - size_t len, i; - len = pw_array_get_len(&impl->permissions, struct permission); - if (len <= global->id) { - size_t diff = global->id - len + 1; - - p = pw_array_add(&impl->permissions, diff * sizeof(struct permission)); - if (p == NULL) - return -ENOMEM; - - for (i = 0; i < diff; i++) - p[i].permissions = -1; - } - - p = pw_array_get_unchecked(&impl->permissions, global->id, struct permission); + p = ensure_permissions(client, global->id); + if (p == NULL) + return -ENOMEM; if (p->permissions == -1) p->permissions = impl->permissions_default; else if (update->only_new) @@ -411,26 +518,6 @@ static int do_permissions(void *data, struct pw_global *global) return 0; } -static uint32_t parse_mask(const char *str) -{ - uint32_t mask = 0; - - while (*str != '\0') { - switch (*str++) { - case 'r': - mask |= PW_PERM_R; - break; - case 'w': - mask |= PW_PERM_W; - break; - case 'x': - mask |= PW_PERM_X; - break; - } - } - return mask; -} - int pw_client_update_permissions(struct pw_client *client, const struct spa_dict *dict) { struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this); @@ -500,3 +587,11 @@ void pw_client_set_busy(struct pw_client *client, bool busy) pw_client_events_busy_changed(client, busy); } } + +void pw_client_set_permissions(struct pw_client *client, uint32_t permissions) +{ + struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this); + pw_log_debug("client %p: permissions %08x", client, permissions); + impl->permissions_default = permissions; + pw_client_set_busy(client, false); +} diff --git a/src/pipewire/client.h b/src/pipewire/client.h index f653f93eb..64eba3578 100644 --- a/src/pipewire/client.h +++ b/src/pipewire/client.h @@ -177,6 +177,8 @@ void pw_client_add_listener(struct pw_client *client, * started and no further processing is allowed to happen for the client */ void pw_client_set_busy(struct pw_client *client, bool busy); +void pw_client_set_permissions(struct pw_client *client, uint32_t permissions); + #ifdef __cplusplus } #endif diff --git a/src/pipewire/interfaces.h b/src/pipewire/interfaces.h index c9d072f66..a25536145 100644 --- a/src/pipewire/interfaces.h +++ b/src/pipewire/interfaces.h @@ -678,7 +678,8 @@ pw_factory_proxy_add_listener(struct pw_factory_proxy *factory, #define PW_VERSION_CLIENT 0 #define PW_CLIENT_PROXY_EVENT_INFO 0 -#define PW_CLIENT_PROXY_EVENT_NUM 1 +#define PW_CLIENT_PROXY_EVENT_PERMISSIONS 1 +#define PW_CLIENT_PROXY_EVENT_NUM 2 /** Client events */ struct pw_client_proxy_events { @@ -690,6 +691,14 @@ struct pw_client_proxy_events { * \param info info about the client */ void (*info) (void *object, struct pw_client_info *info); + /** + * Notify a client permission + * + * Event emited as a result of the get_permissions method. + * + * \param param the parameter + */ + void (*permissions) (void *object, const struct spa_dict *dict); }; /** Client */ @@ -703,7 +712,59 @@ pw_client_proxy_add_listener(struct pw_client_proxy *client, } #define pw_client_resource_info(r,...) pw_resource_notify(r,struct pw_client_proxy_events,info,__VA_ARGS__) +#define pw_client_resource_permissions(r,...) pw_resource_notify(r,struct pw_client_proxy_events,permissions,__VA_ARGS__) +#define PW_CLIENT_PROXY_METHOD_ERROR 0 +#define PW_CLIENT_PROXY_METHOD_GET_PERMISSIONS 1 +#define PW_CLIENT_PROXY_METHOD_UPDATE_PERMISSIONS 2 +#define PW_CLIENT_PROXY_METHOD_NUM 3 + +/** Client methods */ +struct pw_client_proxy_methods { +#define PW_VERSION_CLIENT_PROXY_METHODS 0 + uint32_t version; + + /** + * Send an error to a client + * + * \param id the global id to report the error on + * \param res an errno style error code + * \param error an error string + */ + void (*error) (void *object, uint32_t id, int res, const char *error); + /** + * Get client permissions + * + * A permissions event will be emited with the permissions. + */ + void (*get_permissions) (void *object); + + /** + * Update client permissions + * + * \param dict list of permissions to update + */ + void (*update_permissions) (void *object, const struct spa_dict *dict); +}; + +/** Client permissions */ +static inline void +pw_client_proxy_error(struct pw_client_proxy *client, uint32_t id, int res, const char *error) +{ + pw_proxy_do((struct pw_proxy*)client, struct pw_client_proxy_methods, error, id, res, error); +} + +static inline void +pw_client_proxy_get_permissions(struct pw_client_proxy *client) +{ + pw_proxy_do((struct pw_proxy*)client, struct pw_client_proxy_methods, get_permissions); +} + +static inline void +pw_client_proxy_update_permissions(struct pw_client_proxy *client, const struct spa_dict *dict) +{ + pw_proxy_do((struct pw_proxy*)client, struct pw_client_proxy_methods, update_permissions, dict); +} #define PW_VERSION_LINK 0 diff --git a/src/pipewire/link.c b/src/pipewire/link.c index f19eb334f..049354b3c 100644 --- a/src/pipewire/link.c +++ b/src/pipewire/link.c @@ -230,8 +230,9 @@ static int do_negotiate(struct pw_link *this, uint32_t in_state, uint32_t out_st if (current == NULL || spa_pod_compare(current, format) != 0) { pw_log_debug("link %p: output format change, renegotiate", this); if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) { - spa_debug_format(2, NULL, current); - spa_debug_format(2, NULL, format); + if (current) + spa_debug_pod(2, NULL, current); + spa_debug_pod(2, NULL, format); } pw_node_set_state(output->node, PW_NODE_STATE_SUSPENDED); out_state = PW_PORT_STATE_CONFIGURE; diff --git a/src/tools/pipewire-cli.c b/src/tools/pipewire-cli.c index 775d0be9d..2b3926b8d 100644 --- a/src/tools/pipewire-cli.c +++ b/src/tools/pipewire-cli.c @@ -177,6 +177,7 @@ static bool do_create_link(struct data *data, const char *cmd, char *args, char static bool do_export_node(struct data *data, const char *cmd, char *args, char **error); static bool do_node_params(struct data *data, const char *cmd, char *args, char **error); static bool do_port_params(struct data *data, const char *cmd, char *args, char **error); +static bool do_permissions(struct data *data, const char *cmd, char *args, char **error); static struct command command_list[] = { { "help", "Show this help", do_help }, @@ -194,6 +195,7 @@ static struct command command_list[] = { { "export-node", "Export a local node to the current remote. [remote-var]", do_export_node }, { "node-params", "Enumerate params of a node []", do_node_params }, { "port-params", "Enumerate params of a port []", do_port_params }, + { "permissions", "Set permissions for a client ", do_permissions }, }; static bool do_help(struct data *data, const char *cmd, char *args, char **error) @@ -1133,10 +1135,10 @@ static bool do_port_params(struct data *data, const char *cmd, char *args, char asprintf(error, "%s []", cmd); return false; } - if (n == 2) + if (n < 2) param_id = SPA_PARAM_List; else - param_id = SPA_PARAM_List; + param_id = atoi(a[1]); id = atoi(a[0]); global = pw_map_lookup(&rd->globals, id); @@ -1159,6 +1161,43 @@ static bool do_port_params(struct data *data, const char *cmd, char *args, char return true; } +static bool do_permissions(struct data *data, const char *cmd, char *args, char **error) +{ + struct remote_data *rd = data->current; + char *a[2]; + int n; + uint32_t id; + struct global *global; + struct spa_dict_item items[1]; + + n = pw_split_ip(args, WHITESPACE, 2, a); + if (n < 2) { + asprintf(error, "%s ", cmd); + return false; + } + + id = atoi(a[0]); + global = pw_map_lookup(&rd->globals, id); + if (global == NULL) { + asprintf(error, "%s: unknown global %d", cmd, id); + return false; + } + if (global->type != PW_TYPE_INTERFACE_Client) { + asprintf(error, "object %d is not a client", atoi(a[0])); + return false; + } + if (global->proxy == NULL) { + if (!bind_global(rd, global, error)) + return false; + } + + items[0] = SPA_DICT_ITEM_INIT(PW_CORE_PROXY_PERMISSIONS_GLOBAL, a[1]); + pw_client_proxy_update_permissions((struct pw_client_proxy*)global->proxy, + &SPA_DICT_INIT(items, 1)); + + return true; +} + static bool parse(struct data *data, char *buf, size_t size, char **error) { char *a[2];