1 /* PipeWire
2  *
3  * Copyright © 2018 Wim Taymans
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <errno.h>
26 #include <string.h>
27 
28 #include <spa/utils/string.h>
29 
30 #include "pipewire/impl.h"
31 #include "pipewire/private.h"
32 
33 PW_LOG_TOPIC_EXTERN(log_client);
34 #define PW_LOG_TOPIC_DEFAULT log_client
35 
36 /** \cond */
37 struct impl {
38 	struct pw_impl_client this;
39 	struct spa_hook context_listener;
40 	struct pw_array permissions;
41 	struct spa_hook pool_listener;
42 	unsigned int registered:1;
43 };
44 
45 #define pw_client_resource(r,m,v,...)		pw_resource_call(r,struct pw_client_events,m,v,__VA_ARGS__)
46 #define pw_client_resource_info(r,...)		pw_client_resource(r,info,0,__VA_ARGS__)
47 #define pw_client_resource_permissions(r,...)	pw_client_resource(r,permissions,0,__VA_ARGS__)
48 
49 struct resource_data {
50 	struct pw_resource *resource;
51 	struct spa_hook resource_listener;
52 	struct spa_hook object_listener;
53 	struct pw_impl_client *client;
54 };
55 
56 /** find a specific permission for a global or the default when there is none */
57 static struct pw_permission *
find_permission(struct pw_impl_client * client,uint32_t id)58 find_permission(struct pw_impl_client *client, uint32_t id)
59 {
60 	struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
61 	struct pw_permission *p;
62 	uint32_t idx = id + 1;
63 
64 	if (id == PW_ID_ANY)
65 		goto do_default;
66 
67 	if (!pw_array_check_index(&impl->permissions, idx, struct pw_permission))
68 		goto do_default;
69 
70 	p = pw_array_get_unchecked(&impl->permissions, idx, struct pw_permission);
71 	if (p->permissions == PW_PERM_INVALID)
72 		goto do_default;
73 
74 	return p;
75 
76 do_default:
77 	return pw_array_get_unchecked(&impl->permissions, 0, struct pw_permission);
78 }
79 
ensure_permissions(struct pw_impl_client * client,uint32_t id)80 static struct pw_permission *ensure_permissions(struct pw_impl_client *client, uint32_t id)
81 {
82 	struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
83 	struct pw_permission *p;
84 	uint32_t idx = id + 1;
85 	size_t len, i;
86 
87 	len = pw_array_get_len(&impl->permissions, struct pw_permission);
88 	if (len <= idx) {
89 		size_t diff = idx - len + 1;
90 
91 		p = pw_array_add(&impl->permissions, diff * sizeof(struct pw_permission));
92 		if (p == NULL)
93 			return NULL;
94 
95 		for (i = 0; i < diff; i++) {
96 			p[i] = PW_PERMISSION_INIT(len + i - 1, PW_PERM_INVALID);
97 		}
98 	}
99 	p = pw_array_get_unchecked(&impl->permissions, idx, struct pw_permission);
100 	return p;
101 }
102 
103 /** \endcond */
104 
105 static uint32_t
client_permission_func(struct pw_global * global,struct pw_impl_client * client,void * data)106 client_permission_func(struct pw_global *global,
107 		       struct pw_impl_client *client, void *data)
108 {
109 	struct pw_permission *p = find_permission(client, global->id);
110 	return p->permissions;
111 }
112 
113 struct error_data {
114 	uint32_t id;
115 	int res;
116 	const char *error;
117 };
118 
error_resource(void * object,void * data)119 static int error_resource(void *object, void *data)
120 {
121 	struct pw_resource *r = object;
122 	struct error_data *d = data;
123 	if (r && r->bound_id == d->id)
124 		pw_resource_error(r, d->res, d->error);
125 	return 0;
126 }
127 
client_error(void * object,uint32_t id,int res,const char * error)128 static int client_error(void *object, uint32_t id, int res, const char *error)
129 {
130 	struct resource_data *data = object;
131 	struct pw_impl_client *client = data->client;
132 	struct error_data d = { id, res, error };
133 
134 	pw_log_debug("%p: error for global %d", client, id);
135 	pw_map_for_each(&client->objects, error_resource, &d);
136 	return 0;
137 }
138 
has_key(const char * const keys[],const char * key)139 static bool has_key(const char * const keys[], const char *key)
140 {
141 	int i;
142 	for (i = 0; keys[i]; i++) {
143 		if (spa_streq(keys[i], key))
144 			return true;
145 	}
146 	return false;
147 }
148 
update_properties(struct pw_impl_client * client,const struct spa_dict * dict,bool filter)149 static int update_properties(struct pw_impl_client *client, const struct spa_dict *dict, bool filter)
150 {
151 	static const char * const ignored[] = {
152 		PW_KEY_OBJECT_ID,
153 		NULL
154 	};
155 
156 	struct pw_resource *resource;
157 	int changed = 0;
158 	uint32_t i;
159 	const char *old;
160 
161         for (i = 0; i < dict->n_items; i++) {
162 		if (filter) {
163 			if (spa_strstartswith(dict->items[i].key, "pipewire.") &&
164 			    (old = pw_properties_get(client->properties, dict->items[i].key)) != NULL &&
165 			    (dict->items[i].value == NULL || !spa_streq(old, dict->items[i].value))) {
166 				pw_log_warn("%p: refuse property update '%s' from '%s' to '%s'",
167 						client, dict->items[i].key, old,
168 						dict->items[i].value);
169 				continue;
170 
171 			}
172 			if (has_key(ignored, dict->items[i].key))
173 				continue;
174 		}
175                 changed += pw_properties_set(client->properties, dict->items[i].key, dict->items[i].value);
176 	}
177 	client->info.props = &client->properties->dict;
178 
179 	pw_log_debug("%p: updated %d properties", client, changed);
180 
181 	if (!changed)
182 		return 0;
183 
184 	client->info.change_mask |= PW_CLIENT_CHANGE_MASK_PROPS;
185 
186 	pw_impl_client_emit_info_changed(client, &client->info);
187 
188 	if (client->global)
189 		spa_list_for_each(resource, &client->global->resource_list, link)
190 			pw_client_resource_info(resource, &client->info);
191 
192 	client->info.change_mask = 0;
193 
194 	return changed;
195 }
196 
update_busy(struct pw_impl_client * client)197 static void update_busy(struct pw_impl_client *client)
198 {
199 	struct pw_permission *def;
200 	def = find_permission(client, PW_ID_CORE);
201 	pw_impl_client_set_busy(client, (def->permissions & PW_PERM_R) ? false : true);
202 }
203 
finish_register(struct pw_impl_client * client)204 static int finish_register(struct pw_impl_client *client)
205 {
206 	static const char * const keys[] = {
207 		PW_KEY_ACCESS,
208 		PW_KEY_CLIENT_ACCESS,
209 		PW_KEY_APP_NAME,
210 		NULL
211 	};
212 
213 	struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
214 	struct pw_impl_client *current;
215 
216 	if (impl->registered)
217 		return 0;
218 
219 	impl->registered = true;
220 
221 	current = client->context->current_client;
222 	client->context->current_client = NULL;
223 	pw_context_emit_check_access(client->context, client);
224 	client->context->current_client = current;
225 
226 	update_busy(client);
227 
228 	pw_global_update_keys(client->global, client->info.props, keys);
229 	pw_global_register(client->global);
230 
231 	return 0;
232 }
233 
client_update_properties(void * object,const struct spa_dict * props)234 static int client_update_properties(void *object, const struct spa_dict *props)
235 {
236 	struct resource_data *data = object;
237 	struct pw_impl_client *client = data->client;
238 	int res = update_properties(client, props, true);
239 	finish_register(client);
240 	return res;
241 }
242 
client_get_permissions(void * object,uint32_t index,uint32_t num)243 static int client_get_permissions(void *object, uint32_t index, uint32_t num)
244 {
245 	struct resource_data *data = object;
246 	struct pw_resource *resource = data->resource;
247 	struct pw_impl_client *client = data->client;
248 	struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
249 	size_t len;
250 
251 	len = pw_array_get_len(&impl->permissions, struct pw_permission);
252 	if ((size_t)index >= len)
253 		num = 0;
254 	else if ((size_t)index + (size_t)num >= len)
255 		num = len - index;
256 
257 	pw_client_resource_permissions(resource, index,
258 			num, pw_array_get_unchecked(&impl->permissions, index, struct pw_permission));
259 	return 0;
260 }
261 
client_update_permissions(void * object,uint32_t n_permissions,const struct pw_permission * permissions)262 static int client_update_permissions(void *object,
263 		uint32_t n_permissions, const struct pw_permission *permissions)
264 {
265 	struct resource_data *data = object;
266 	struct pw_impl_client *client = data->client;
267 	return pw_impl_client_update_permissions(client, n_permissions, permissions);
268 }
269 
270 static const struct pw_client_methods client_methods = {
271 	PW_VERSION_CLIENT_METHODS,
272 	.error = client_error,
273 	.update_properties = client_update_properties,
274 	.get_permissions = client_get_permissions,
275 	.update_permissions = client_update_permissions
276 };
277 
client_unbind_func(void * data)278 static void client_unbind_func(void *data)
279 {
280 	struct resource_data *d = data;
281 	struct pw_resource *resource = d->resource;
282 	spa_hook_remove(&d->resource_listener);
283 	spa_hook_remove(&d->object_listener);
284 	if (resource->id == 1)
285 		resource->client->client_resource = NULL;
286 }
287 
288 static const struct pw_resource_events resource_events = {
289 	PW_VERSION_RESOURCE_EVENTS,
290 	.destroy = client_unbind_func,
291 };
292 
293 static int
global_bind(void * _data,struct pw_impl_client * client,uint32_t permissions,uint32_t version,uint32_t id)294 global_bind(void *_data, struct pw_impl_client *client, uint32_t permissions,
295 		 uint32_t version, uint32_t id)
296 {
297 	struct pw_impl_client *this = _data;
298 	struct pw_global *global = this->global;
299 	struct pw_resource *resource;
300 	struct resource_data *data;
301 
302 	resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data));
303 	if (resource == NULL)
304 		goto error_resource;
305 
306 	data = pw_resource_get_user_data(resource);
307 	data->resource = resource;
308 	data->client = this;
309 	pw_resource_add_listener(resource,
310 			&data->resource_listener,
311 			&resource_events, data);
312 	pw_resource_add_object_listener(resource,
313 			&data->object_listener,
314 			&client_methods, data);
315 
316 	pw_log_debug("%p: bound to %d", this, resource->id);
317 	pw_global_add_resource(global, resource);
318 
319 	if (resource->id == 1)
320 		client->client_resource = resource;
321 
322 	this->info.change_mask = PW_CLIENT_CHANGE_MASK_ALL;
323 	pw_client_resource_info(resource, &this->info);
324 	this->info.change_mask = 0;
325 
326 	return 0;
327 
328 error_resource:
329 	pw_log_error("%p: can't create client resource: %m", this);
330 	return -errno;
331 }
332 
pool_added(void * data,struct pw_memblock * block)333 static void pool_added(void *data, struct pw_memblock *block)
334 {
335 	struct impl *impl = data;
336 	struct pw_impl_client *client = &impl->this;
337 
338 	pw_log_debug("%p: added block %d", client, block->id);
339 	if (client->core_resource) {
340 		pw_core_resource_add_mem(client->core_resource,
341 				block->id, block->type, block->fd,
342 				block->flags & PW_MEMBLOCK_FLAG_READWRITE);
343 	}
344 }
345 
pool_removed(void * data,struct pw_memblock * block)346 static void pool_removed(void *data, struct pw_memblock *block)
347 {
348 	struct impl *impl = data;
349 	struct pw_impl_client *client = &impl->this;
350 	pw_log_debug("%p: removed block %d", client, block->id);
351 	if (client->core_resource)
352 		pw_core_resource_remove_mem(client->core_resource, block->id);
353 }
354 
355 static const struct pw_mempool_events pool_events = {
356 	PW_VERSION_MEMPOOL_EVENTS,
357 	.added = pool_added,
358 	.removed = pool_removed,
359 };
360 
361 static void
context_global_removed(void * data,struct pw_global * global)362 context_global_removed(void *data, struct pw_global *global)
363 {
364 	struct impl *impl = data;
365 	struct pw_impl_client *client = &impl->this;
366 	struct pw_permission *p;
367 
368 	p = find_permission(client, global->id);
369 	pw_log_debug("%p: global %d removed, %p", client, global->id, p);
370 	if (p->id != PW_ID_ANY)
371 		p->permissions = PW_PERM_INVALID;
372 }
373 
374 static const struct pw_context_events context_events = {
375 	PW_VERSION_CONTEXT_EVENTS,
376 	.global_removed = context_global_removed,
377 };
378 
379 /** Make a new client object
380  *
381  * \param core a \ref pw_context object to register the client with
382  * \param properties optional client properties, ownership is taken
383  * \return a newly allocated client object
384  *
385  */
386 SPA_EXPORT
pw_context_create_client(struct pw_impl_core * core,struct pw_protocol * protocol,struct pw_properties * properties,size_t user_data_size)387 struct pw_impl_client *pw_context_create_client(struct pw_impl_core *core,
388 				struct pw_protocol *protocol,
389 				struct pw_properties *properties,
390 				size_t user_data_size)
391 {
392 	struct pw_impl_client *this;
393 	struct impl *impl;
394 	struct pw_permission *p;
395 	int res;
396 
397 	impl = calloc(1, sizeof(struct impl) + user_data_size);
398 	if (impl == NULL) {
399 		res = -errno;
400 		goto error_cleanup;
401 	}
402 
403 	this = &impl->this;
404 	pw_log_debug("%p: new", this);
405 
406 	this->context = core->context;
407 	this->core = core;
408 	this->protocol = protocol;
409 
410 	if (properties == NULL)
411 		properties = pw_properties_new(NULL, NULL);
412 	if (properties == NULL) {
413 		res = -errno;
414 		goto error_free;
415 	}
416 
417 	pw_array_init(&impl->permissions, 1024);
418 	p = pw_array_add(&impl->permissions, sizeof(struct pw_permission));
419 	if (p == NULL) {
420 		res = -errno;
421 		goto error_clear_array;
422 	}
423 	p->id = PW_ID_ANY;
424 	p->permissions = 0;
425 
426 	this->pool = pw_mempool_new(NULL);
427 	if (this->pool == NULL) {
428 		res = -errno;
429 		goto error_clear_array;
430 	}
431 	pw_mempool_add_listener(this->pool, &impl->pool_listener, &pool_events, impl);
432 
433 	this->properties = properties;
434 	this->permission_func = client_permission_func;
435 	this->permission_data = impl;
436 
437 	if (user_data_size > 0)
438 		this->user_data = SPA_PTROFF(impl, sizeof(struct impl), void);
439 
440 	spa_hook_list_init(&this->listener_list);
441 
442 	pw_map_init(&this->objects, 0, 32);
443 
444 	pw_context_add_listener(this->context, &impl->context_listener, &context_events, impl);
445 
446 	this->info.props = &this->properties->dict;
447 
448 	return this;
449 
450 error_clear_array:
451 	pw_array_clear(&impl->permissions);
452 error_free:
453 	free(impl);
454 error_cleanup:
455 	pw_properties_free(properties);
456 	errno = -res;
457 	return NULL;
458 }
459 
global_destroy(void * object)460 static void global_destroy(void *object)
461 {
462 	struct pw_impl_client *client = object;
463 	spa_hook_remove(&client->global_listener);
464 	client->global = NULL;
465 	pw_impl_client_destroy(client);
466 }
467 
468 static const struct pw_global_events global_events = {
469 	PW_VERSION_GLOBAL_EVENTS,
470 	.destroy = global_destroy,
471 };
472 
473 SPA_EXPORT
pw_impl_client_register(struct pw_impl_client * client,struct pw_properties * properties)474 int pw_impl_client_register(struct pw_impl_client *client,
475 		       struct pw_properties *properties)
476 {
477 	static const char * const keys[] = {
478 		PW_KEY_OBJECT_SERIAL,
479 		PW_KEY_MODULE_ID,
480 		PW_KEY_PROTOCOL,
481 		PW_KEY_SEC_PID,
482 		PW_KEY_SEC_UID,
483 		PW_KEY_SEC_GID,
484 		PW_KEY_SEC_LABEL,
485 		NULL
486 	};
487 
488 	struct pw_context *context = client->context;
489 
490 	if (client->registered)
491 		goto error_existed;
492 
493 	pw_log_debug("%p: register", client);
494 
495 	client->global = pw_global_new(context,
496 				       PW_TYPE_INTERFACE_Client,
497 				       PW_VERSION_CLIENT,
498 				       properties,
499 				       global_bind,
500 				       client);
501 	if (client->global == NULL)
502 		return -errno;
503 
504 	spa_list_append(&context->client_list, &client->link);
505 	client->registered = true;
506 
507 	client->info.id = client->global->id;
508 	pw_properties_setf(client->properties, PW_KEY_OBJECT_ID, "%d", client->info.id);
509 	pw_properties_setf(client->properties, PW_KEY_OBJECT_SERIAL, "%"PRIu64,
510 			pw_global_get_serial(client->global));
511 	client->info.props = &client->properties->dict;
512 	pw_global_add_listener(client->global, &client->global_listener, &global_events, client);
513 
514 	pw_global_update_keys(client->global, client->info.props, keys);
515 
516 	pw_impl_client_emit_initialized(client);
517 
518 	return 0;
519 
520 error_existed:
521 	pw_properties_free(properties);
522 	return -EEXIST;
523 }
524 
525 SPA_EXPORT
pw_impl_client_get_context(struct pw_impl_client * client)526 struct pw_context *pw_impl_client_get_context(struct pw_impl_client *client)
527 {
528 	return client->core->context;
529 }
530 
531 SPA_EXPORT
pw_impl_client_get_protocol(struct pw_impl_client * client)532 struct pw_protocol *pw_impl_client_get_protocol(struct pw_impl_client *client)
533 {
534 	return client->protocol;
535 }
536 
537 SPA_EXPORT
pw_impl_client_get_core_resource(struct pw_impl_client * client)538 struct pw_resource *pw_impl_client_get_core_resource(struct pw_impl_client *client)
539 {
540 	return client->core_resource;
541 }
542 
543 SPA_EXPORT
pw_impl_client_find_resource(struct pw_impl_client * client,uint32_t id)544 struct pw_resource *pw_impl_client_find_resource(struct pw_impl_client *client, uint32_t id)
545 {
546 	return pw_map_lookup(&client->objects, id);
547 }
548 
549 SPA_EXPORT
pw_impl_client_get_global(struct pw_impl_client * client)550 struct pw_global *pw_impl_client_get_global(struct pw_impl_client *client)
551 {
552 	return client->global;
553 }
554 
555 SPA_EXPORT
pw_impl_client_get_properties(struct pw_impl_client * client)556 const struct pw_properties *pw_impl_client_get_properties(struct pw_impl_client *client)
557 {
558 	return client->properties;
559 }
560 
561 SPA_EXPORT
pw_impl_client_get_user_data(struct pw_impl_client * client)562 void *pw_impl_client_get_user_data(struct pw_impl_client *client)
563 {
564 	return client->user_data;
565 }
566 
destroy_resource(void * object,void * data)567 static int destroy_resource(void *object, void *data)
568 {
569 	if (object)
570 		pw_resource_destroy(object);
571 	return 0;
572 }
573 
574 
575 /** Destroy a client object
576  *
577  * \param client the client to destroy
578  *
579  */
580 SPA_EXPORT
pw_impl_client_destroy(struct pw_impl_client * client)581 void pw_impl_client_destroy(struct pw_impl_client *client)
582 {
583 	struct impl *impl = SPA_CONTAINER_OF(client, struct impl, this);
584 
585 	pw_log_debug("%p: destroy", client);
586 	pw_impl_client_emit_destroy(client);
587 
588 	spa_hook_remove(&impl->context_listener);
589 
590 	if (client->registered)
591 		spa_list_remove(&client->link);
592 
593 	pw_map_for_each(&client->objects, destroy_resource, client);
594 
595 	if (client->global) {
596 		spa_hook_remove(&client->global_listener);
597 		pw_global_destroy(client->global);
598 	}
599 
600 	pw_log_debug("%p: free", impl);
601 	pw_impl_client_emit_free(client);
602 
603 	spa_hook_list_clean(&client->listener_list);
604 
605 	pw_map_clear(&client->objects);
606 	pw_array_clear(&impl->permissions);
607 
608 	spa_hook_remove(&impl->pool_listener);
609 	pw_mempool_destroy(client->pool);
610 
611 	pw_properties_free(client->properties);
612 
613 	free(impl);
614 }
615 
616 SPA_EXPORT
pw_impl_client_add_listener(struct pw_impl_client * client,struct spa_hook * listener,const struct pw_impl_client_events * events,void * data)617 void pw_impl_client_add_listener(struct pw_impl_client *client,
618 			    struct spa_hook *listener,
619 			    const struct pw_impl_client_events *events,
620 			    void *data)
621 {
622 	spa_hook_list_append(&client->listener_list, listener, events, data);
623 }
624 
625 SPA_EXPORT
pw_impl_client_get_info(struct pw_impl_client * client)626 const struct pw_client_info *pw_impl_client_get_info(struct pw_impl_client *client)
627 {
628 	return &client->info;
629 }
630 
631 /** Update client properties
632  *
633  * \param client the client
634  * \param dict a struct spa_dict with properties
635  *
636  * Add all properties in \a dict to the client properties. Existing
637  * properties are overwritten. Items can be removed by setting the value
638  * to NULL.
639  *
640  */
641 SPA_EXPORT
pw_impl_client_update_properties(struct pw_impl_client * client,const struct spa_dict * dict)642 int pw_impl_client_update_properties(struct pw_impl_client *client, const struct spa_dict *dict)
643 {
644 	int res = update_properties(client, dict, false);
645 	finish_register(client);
646 	return res;
647 }
648 
649 SPA_EXPORT
pw_impl_client_update_permissions(struct pw_impl_client * client,uint32_t n_permissions,const struct pw_permission * permissions)650 int pw_impl_client_update_permissions(struct pw_impl_client *client,
651 		uint32_t n_permissions, const struct pw_permission *permissions)
652 {
653 	struct pw_impl_core *core = client->core;
654 	struct pw_context *context = core->context;
655 	struct pw_permission *def;
656 	uint32_t i;
657 
658 	if ((def = find_permission(client, PW_ID_ANY)) == NULL)
659 		return -EIO;
660 
661 	for (i = 0; i < n_permissions; i++) {
662 		struct pw_permission *p;
663 		uint32_t old_perm, new_perm;
664 		struct pw_global *global;
665 
666 		if (permissions[i].id == PW_ID_ANY) {
667 			old_perm = def->permissions;
668 			new_perm = permissions[i].permissions;
669 
670 			if (context->current_client == client)
671 				new_perm &= old_perm;
672 
673 			pw_log_debug("%p: set default permissions %08x -> %08x",
674 					client, old_perm, new_perm);
675 
676 			def->permissions = new_perm;
677 
678 			spa_list_for_each(global, &context->global_list, link) {
679 				if (global->id == client->info.id)
680 					continue;
681 				p = find_permission(client, global->id);
682 				if (p->id != PW_ID_ANY)
683 					continue;
684 				pw_global_update_permissions(global, client, old_perm, new_perm);
685 			}
686 		}
687 		else  {
688 			struct pw_global *global;
689 
690 			global = pw_context_find_global(context, permissions[i].id);
691 			if (global == NULL || global->id != permissions[i].id) {
692 				pw_log_warn("%p: invalid global %d", client, permissions[i].id);
693 				continue;
694 			}
695 			p = ensure_permissions(client, permissions[i].id);
696 			if (p == NULL) {
697 				pw_log_warn("%p: can't ensure permission: %m", client);
698 				return -errno;
699 			}
700 			if ((def = find_permission(client, PW_ID_ANY)) == NULL)
701 				return -EIO;
702 			old_perm = p->permissions == PW_PERM_INVALID ? def->permissions : p->permissions;
703 			new_perm = permissions[i].permissions;
704 
705 			if (context->current_client == client)
706 				new_perm &= old_perm;
707 
708 			pw_log_debug("%p: set global %d permissions %08x -> %08x",
709 					client, global->id, old_perm, new_perm);
710 
711 			p->permissions = new_perm;
712 			pw_global_update_permissions(global, client, old_perm, new_perm);
713 		}
714 	}
715 	update_busy(client);
716 	return 0;
717 }
718 
719 SPA_EXPORT
pw_impl_client_set_busy(struct pw_impl_client * client,bool busy)720 void pw_impl_client_set_busy(struct pw_impl_client *client, bool busy)
721 {
722 	if (client->busy != busy) {
723 		pw_log_debug("%p: busy %d", client, busy);
724 		client->busy = busy;
725 		pw_impl_client_emit_busy_changed(client, busy);
726 	}
727 }
728 
729 SPA_EXPORT
pw_impl_client_check_permissions(struct pw_impl_client * client,uint32_t global_id,uint32_t permissions)730 int pw_impl_client_check_permissions(struct pw_impl_client *client,
731 		uint32_t global_id, uint32_t permissions)
732 {
733 	struct pw_context *context = client->context;
734 	struct pw_global *global;
735 	uint32_t perms;
736 
737 	if ((global = pw_context_find_global(context, global_id)) == NULL)
738 		return -ENOENT;
739 
740 	perms = pw_global_get_permissions(global, client);
741 	if ((perms & permissions) != permissions)
742 		return -EPERM;
743 
744 	return 0;
745 }
746