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