protocol: add more methods on client

Add method to get and set permissions on a client
Add method to send error to client.
This commit is contained in:
Wim Taymans 2018-10-27 17:31:03 +01:00
parent bbf718cc98
commit e026f55c97
6 changed files with 410 additions and 46 deletions

View file

@ -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,

View file

@ -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=<global-id>:[r][w][x] */
len = strcspn(str, ":");
if (len == 0)
continue;
global_id = atoi(str);
global = pw_core_find_global(client->core, global_id);
if (global == NULL) {
pw_log_warn("client %p: invalid global %d", client, global_id);
continue;
}
p = ensure_permissions(client, global_id);
old_perm = p->permissions == -1 ? impl->permissions_default : p->permissions;
new_perm = parse_mask(str + len);
pw_log_debug("client %p: %08x %08x", client, old_perm, new_perm);
p->permissions = new_perm;
if (PW_PERM_IS_R(old_perm) && !PW_PERM_IS_R(new_perm)) {
pw_global_revoke(global, client);
}
else if (!PW_PERM_IS_R(old_perm) && PW_PERM_IS_R(new_perm)) {
pw_global_grant(global, client);
}
}
}
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);
}

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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. <node-id> [remote-var]", do_export_node },
{ "node-params", "Enumerate params of a node <node-id> [<param-id-name>]", do_node_params },
{ "port-params", "Enumerate params of a port <port-id> [<param-id-name>]", do_port_params },
{ "permissions", "Set permissions for a client <client-id> <permissions>", 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 <object-id> [<param-id-name>]", 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 <client-id> <permission>", 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];