1 /* PipeWire
2  *
3  * Copyright © 2020 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 "manager.h"
26 
27 #include <spa/pod/iter.h>
28 #include <spa/pod/parser.h>
29 #include <spa/utils/result.h>
30 #include <spa/utils/string.h>
31 #include <pipewire/extensions/metadata.h>
32 
33 #include "log.h"
34 #include "module-protocol-pulse/server.h"
35 
36 #define MAX_PARAMS 32
37 
38 #define manager_emit_sync(m) spa_hook_list_call(&m->hooks, struct pw_manager_events, sync, 0)
39 #define manager_emit_added(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, added, 0, o)
40 #define manager_emit_updated(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, updated, 0, o)
41 #define manager_emit_removed(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, removed, 0, o)
42 #define manager_emit_metadata(m,o,s,k,t,v) spa_hook_list_call(&m->hooks, struct pw_manager_events, metadata,0,o,s,k,t,v)
43 #define manager_emit_disconnect(m) spa_hook_list_call(&m->hooks, struct pw_manager_events, disconnect, 0)
44 
45 struct object;
46 
47 struct manager {
48 	struct pw_manager this;
49 
50 	struct spa_hook core_listener;
51 	struct spa_hook registry_listener;
52 	int sync_seq;
53 
54 	struct spa_hook_list hooks;
55 };
56 
57 struct object_info {
58 	const char *type;
59 	uint32_t version;
60 	const void *events;
61 	void (*init) (struct object *object);
62 	void (*destroy) (struct object *object);
63 };
64 
65 struct object_data {
66 	struct spa_list link;
67 	const char *id;
68 	size_t size;
69 };
70 
71 struct object {
72 	struct pw_manager_object this;
73 
74 	struct manager *manager;
75 
76 	const struct object_info *info;
77 
78 	struct spa_list pending_list;
79 
80 	struct spa_hook proxy_listener;
81 	struct spa_hook object_listener;
82 
83 	int param_seq[MAX_PARAMS];
84 
85 	struct spa_list data_list;
86 };
87 
core_sync(struct manager * m)88 static int core_sync(struct manager *m)
89 {
90 	m->sync_seq = pw_core_sync(m->this.core, PW_ID_CORE, m->sync_seq);
91 	pw_log_debug("sync start %u", m->sync_seq);
92 	return m->sync_seq;
93 }
94 
clear_params(struct spa_list * param_list,uint32_t id)95 static uint32_t clear_params(struct spa_list *param_list, uint32_t id)
96 {
97 	struct pw_manager_param *p, *t;
98 	uint32_t count = 0;
99 
100 	spa_list_for_each_safe(p, t, param_list, link) {
101 		if (id == SPA_ID_INVALID || p->id == id) {
102 			spa_list_remove(&p->link);
103 			free(p);
104 			count++;
105 		}
106 	}
107 	return count;
108 }
109 
add_param(struct spa_list * params,int seq,int * param_seq,uint32_t id,const struct spa_pod * param)110 static struct pw_manager_param *add_param(struct spa_list *params,
111 		int seq, int *param_seq, uint32_t id, const struct spa_pod *param)
112 {
113 	struct pw_manager_param *p;
114 
115 	if (id == SPA_ID_INVALID) {
116 		if (param == NULL || !spa_pod_is_object(param)) {
117 			errno = EINVAL;
118 			return NULL;
119 		}
120 		id = SPA_POD_OBJECT_ID(param);
121 	}
122 
123 	if (id >= MAX_PARAMS) {
124 		pw_log_error("too big param id %d", id);
125 		errno = EINVAL;
126 		return NULL;
127 	}
128 
129 	if (seq != param_seq[id]) {
130 		pw_log_debug("ignoring param %d, seq:%d != current_seq:%d",
131 				id, seq, param_seq[id]);
132 		errno = EBUSY;
133 		return NULL;
134 	}
135 
136 	p = malloc(sizeof(*p) + (param != NULL ? SPA_POD_SIZE(param) : 0));
137 	if (p == NULL)
138 		return NULL;
139 
140 	p->id = id;
141 	if (param != NULL) {
142 		p->param = SPA_PTROFF(p, sizeof(*p), struct spa_pod);
143 		memcpy(p->param, param, SPA_POD_SIZE(param));
144 	} else {
145 		clear_params(params, id);
146 		p->param = NULL;
147 	}
148 	spa_list_append(params, &p->link);
149 
150 	return p;
151 }
152 
has_param(struct spa_list * param_list,struct pw_manager_param * p)153 static bool has_param(struct spa_list *param_list, struct pw_manager_param *p)
154 {
155 	struct pw_manager_param *t;
156 	spa_list_for_each(t, param_list, link) {
157 		if (p->id == t->id &&
158 		   SPA_POD_SIZE(p->param) == SPA_POD_SIZE(t->param) &&
159 		   memcmp(p->param, t->param, SPA_POD_SIZE(p->param)) == 0)
160 			return true;
161 	}
162 	return false;
163 }
164 
165 
find_object(struct manager * m,uint32_t id)166 static struct object *find_object(struct manager *m, uint32_t id)
167 {
168 	struct object *o;
169 	spa_list_for_each(o, &m->this.object_list, this.link) {
170 		if (o->this.id == id)
171 			return o;
172 	}
173 	return NULL;
174 }
175 
object_update_params(struct object * o)176 static void object_update_params(struct object *o)
177 {
178 	struct pw_manager_param *p;
179 
180 	spa_list_consume(p, &o->pending_list, link) {
181 		spa_list_remove(&p->link);
182 		if (p->param == NULL) {
183 			clear_params(&o->this.param_list, p->id);
184 			free(p);
185 		} else {
186 			spa_list_append(&o->this.param_list, &p->link);
187 		}
188 	}
189 }
190 
object_destroy(struct object * o)191 static void object_destroy(struct object *o)
192 {
193 	struct manager *m = o->manager;
194 	struct object_data *d;
195 	spa_list_remove(&o->this.link);
196 	m->this.n_objects--;
197 	if (o->this.proxy)
198 		pw_proxy_destroy(o->this.proxy);
199 	pw_properties_free(o->this.props);
200 	if (o->this.message_object_path)
201 		free(o->this.message_object_path);
202 	clear_params(&o->this.param_list, SPA_ID_INVALID);
203 	clear_params(&o->pending_list, SPA_ID_INVALID);
204 	spa_list_consume(d, &o->data_list, link) {
205 		spa_list_remove(&d->link);
206 		free(d);
207 	}
208 	free(o);
209 }
210 
211 /* core */
212 static const struct object_info core_info = {
213 	.type = PW_TYPE_INTERFACE_Core,
214 	.version = PW_VERSION_CORE,
215 };
216 
217 /* client */
client_event_info(void * object,const struct pw_client_info * info)218 static void client_event_info(void *object, const struct pw_client_info *info)
219 {
220 	struct object *o = object;
221 	int changed = 0;
222 
223 	pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask);
224 
225 	info = o->this.info = pw_client_info_merge(o->this.info, info, o->this.changed == 0);
226 
227 	if (info->change_mask & PW_CLIENT_CHANGE_MASK_PROPS)
228 		changed++;
229 
230 	if (changed) {
231 		o->this.changed += changed;
232 		core_sync(o->manager);
233 	}
234 }
235 
236 static const struct pw_client_events client_events = {
237 	PW_VERSION_CLIENT_EVENTS,
238 	.info = client_event_info,
239 };
240 
client_destroy(struct object * o)241 static void client_destroy(struct object *o)
242 {
243 	if (o->this.info) {
244 		pw_client_info_free(o->this.info);
245 		o->this.info = NULL;
246 	}
247 }
248 
249 static const struct object_info client_info = {
250 	.type = PW_TYPE_INTERFACE_Client,
251 	.version = PW_VERSION_CLIENT,
252 	.events = &client_events,
253 	.destroy = client_destroy,
254 };
255 
256 /* module */
module_event_info(void * object,const struct pw_module_info * info)257 static void module_event_info(void *object, const struct pw_module_info *info)
258 {
259 	struct object *o = object;
260 	int changed = 0;
261 
262 	pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask);
263 
264 	info = o->this.info = pw_module_info_merge(o->this.info, info, o->this.changed == 0);
265 
266 	if (info->change_mask & PW_MODULE_CHANGE_MASK_PROPS)
267 		changed++;
268 
269 	if (changed) {
270 		o->this.changed += changed;
271 		core_sync(o->manager);
272 	}
273 }
274 
275 static const struct pw_module_events module_events = {
276 	PW_VERSION_MODULE_EVENTS,
277 	.info = module_event_info,
278 };
279 
module_destroy(struct object * o)280 static void module_destroy(struct object *o)
281 {
282 	if (o->this.info) {
283 		pw_module_info_free(o->this.info);
284 		o->this.info = NULL;
285 	}
286 }
287 
288 static const struct object_info module_info = {
289 	.type = PW_TYPE_INTERFACE_Module,
290 	.version = PW_VERSION_MODULE,
291 	.events = &module_events,
292 	.destroy = module_destroy,
293 };
294 
295 /* device */
device_event_info(void * object,const struct pw_device_info * info)296 static void device_event_info(void *object, const struct pw_device_info *info)
297 {
298 	struct object *o = object;
299 	uint32_t i, changed = 0;
300 
301 	pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask);
302 
303 	info = o->this.info = pw_device_info_merge(o->this.info, info, o->this.changed == 0);
304 
305 	if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS)
306 		changed++;
307 
308 	if (info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) {
309 		for (i = 0; i < info->n_params; i++) {
310 			uint32_t id = info->params[i].id;
311 			int res;
312 
313 			if (info->params[i].user == 0)
314 				continue;
315 			info->params[i].user = 0;
316 
317 			if (id >= MAX_PARAMS) {
318 				pw_log_error("too big param id %d", id);
319 				continue;
320 			}
321 
322 			switch (id) {
323 			case SPA_PARAM_EnumProfile:
324 			case SPA_PARAM_Profile:
325 			case SPA_PARAM_EnumRoute:
326 				changed++;
327 				break;
328 			case SPA_PARAM_Route:
329 				break;
330 			}
331 			add_param(&o->pending_list, o->param_seq[id], o->param_seq, id, NULL);
332 			if (!(info->params[i].flags & SPA_PARAM_INFO_READ))
333 				continue;
334 
335 			res = pw_device_enum_params((struct pw_device*)o->this.proxy,
336 					++o->param_seq[id], id, 0, -1, NULL);
337 			if (SPA_RESULT_IS_ASYNC(res))
338 				o->param_seq[id] = res;
339 		}
340 	}
341 	if (changed) {
342 		o->this.changed += changed;
343 		core_sync(o->manager);
344 	}
345 }
find_device(struct manager * m,uint32_t card_id,uint32_t device)346 static struct object *find_device(struct manager *m, uint32_t card_id, uint32_t device)
347 {
348 	struct object *o;
349 
350 	spa_list_for_each(o, &m->this.object_list, this.link) {
351 		struct pw_node_info *info;
352 		const char *str;
353 
354 		if (!spa_streq(o->this.type, PW_TYPE_INTERFACE_Node))
355 			continue;
356 
357 		if ((info = o->this.info) != NULL &&
358 		    (str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)) != NULL &&
359 		    (uint32_t)atoi(str) == card_id &&
360 		    (str = spa_dict_lookup(info->props, "card.profile.device")) != NULL &&
361 		    (uint32_t)atoi(str) == device)
362 			return o;
363 	}
364 	return NULL;
365 }
366 
device_event_param(void * object,int seq,uint32_t id,uint32_t index,uint32_t next,const struct spa_pod * param)367 static void device_event_param(void *object, int seq,
368 		uint32_t id, uint32_t index, uint32_t next,
369 		const struct spa_pod *param)
370 {
371 	struct object *o = object, *dev;
372 	struct manager *m = o->manager;
373 	struct pw_manager_param *p;
374 
375 	p = add_param(&o->pending_list, seq, o->param_seq, id, param);
376 	if (p == NULL)
377 		return;
378 
379 	if (id == SPA_PARAM_Route && !has_param(&o->this.param_list, p)) {
380 		uint32_t idx, device;
381 		if (spa_pod_parse_object(param,
382 				SPA_TYPE_OBJECT_ParamRoute, NULL,
383 				SPA_PARAM_ROUTE_index, SPA_POD_Int(&idx),
384 				SPA_PARAM_ROUTE_device,  SPA_POD_Int(&device)) < 0)
385 			return;
386 
387 		if ((dev = find_device(m, o->this.id, device)) != NULL) {
388 			dev->this.changed++;
389 			core_sync(o->manager);
390 		}
391 	}
392 }
393 
394 static const struct pw_device_events device_events = {
395 	PW_VERSION_DEVICE_EVENTS,
396 	.info = device_event_info,
397 	.param = device_event_param,
398 };
399 
device_destroy(struct object * o)400 static void device_destroy(struct object *o)
401 {
402 	if (o->this.info) {
403 		pw_device_info_free(o->this.info);
404 		o->this.info = NULL;
405 	}
406 }
407 
408 static const struct object_info device_info = {
409 	.type = PW_TYPE_INTERFACE_Device,
410 	.version = PW_VERSION_DEVICE,
411 	.events = &device_events,
412 	.destroy = device_destroy,
413 };
414 
415 /* node */
node_event_info(void * object,const struct pw_node_info * info)416 static void node_event_info(void *object, const struct pw_node_info *info)
417 {
418 	struct object *o = object;
419 	uint32_t i, changed = 0;
420 
421 	pw_log_debug("object %p: id:%d change-mask:%08"PRIx64, o, o->this.id, info->change_mask);
422 
423 	info = o->this.info = pw_node_info_merge(o->this.info, info, o->this.changed == 0);
424 
425 	if (info->change_mask & PW_NODE_CHANGE_MASK_STATE)
426 		changed++;
427 
428 	if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS)
429 		changed++;
430 
431 	if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) {
432 		for (i = 0; i < info->n_params; i++) {
433 			uint32_t id = info->params[i].id;
434 			int res;
435 
436 			if (info->params[i].user == 0)
437 				continue;
438 			info->params[i].user = 0;
439 
440 			if (id >= MAX_PARAMS) {
441 				pw_log_error("too big param id %d", id);
442 				continue;
443 			}
444 
445 			changed++;
446 			add_param(&o->pending_list, o->param_seq[id], o->param_seq, id, NULL);
447 			if (!(info->params[i].flags & SPA_PARAM_INFO_READ))
448 				continue;
449 
450 			res = pw_node_enum_params((struct pw_node*)o->this.proxy,
451 					++o->param_seq[id], id, 0, -1, NULL);
452 			if (SPA_RESULT_IS_ASYNC(res))
453 				o->param_seq[id] = res;
454 		}
455 	}
456 	if (changed) {
457 		o->this.changed += changed;
458 		core_sync(o->manager);
459 	}
460 }
461 
node_event_param(void * object,int seq,uint32_t id,uint32_t index,uint32_t next,const struct spa_pod * param)462 static void node_event_param(void *object, int seq,
463 		uint32_t id, uint32_t index, uint32_t next,
464 		const struct spa_pod *param)
465 {
466 	struct object *o = object;
467 	add_param(&o->pending_list, seq, o->param_seq, id, param);
468 }
469 
470 static const struct pw_node_events node_events = {
471 	PW_VERSION_NODE_EVENTS,
472 	.info = node_event_info,
473 	.param = node_event_param,
474 };
475 
node_destroy(struct object * o)476 static void node_destroy(struct object *o)
477 {
478 	if (o->this.info) {
479 		pw_node_info_free(o->this.info);
480 		o->this.info = NULL;
481 	}
482 }
483 
484 static const struct object_info node_info = {
485 	.type = PW_TYPE_INTERFACE_Node,
486 	.version = PW_VERSION_NODE,
487 	.events = &node_events,
488 	.destroy = node_destroy,
489 };
490 
491 /* link */
492 static const struct object_info link_info = {
493 	.type = PW_TYPE_INTERFACE_Link,
494 	.version = PW_VERSION_LINK,
495 };
496 
497 /* metadata */
metadata_property(void * object,uint32_t subject,const char * key,const char * type,const char * value)498 static int metadata_property(void *object,
499 			uint32_t subject,
500 			const char *key,
501 			const char *type,
502 			const char *value)
503 {
504 	struct object *o = object;
505 	struct manager *m = o->manager;
506 	manager_emit_metadata(m, &o->this, subject, key, type, value);
507 	return 0;
508 }
509 
510 static const struct pw_metadata_events metadata_events = {
511 	PW_VERSION_METADATA_EVENTS,
512 	.property = metadata_property,
513 };
514 
metadata_init(struct object * object)515 static void metadata_init(struct object *object)
516 {
517 	struct object *o = object;
518 	struct manager *m = o->manager;
519 	o->this.creating = false;
520 	manager_emit_added(m, &o->this);
521 }
522 
523 static const struct object_info metadata_info = {
524 	.type = PW_TYPE_INTERFACE_Metadata,
525 	.version = PW_VERSION_METADATA,
526 	.events = &metadata_events,
527 	.init = metadata_init,
528 };
529 
530 static const struct object_info *objects[] =
531 {
532 	&core_info,
533 	&module_info,
534 	&client_info,
535 	&device_info,
536 	&node_info,
537 	&link_info,
538 	&metadata_info,
539 };
540 
find_info(const char * type,uint32_t version)541 static const struct object_info *find_info(const char *type, uint32_t version)
542 {
543 	size_t i;
544 	for (i = 0; i < SPA_N_ELEMENTS(objects); i++) {
545 		if (spa_streq(objects[i]->type, type) &&
546 		    objects[i]->version <= version)
547 			return objects[i];
548 	}
549 	return NULL;
550 }
551 
552 static void
destroy_removed(void * data)553 destroy_removed(void *data)
554 {
555 	struct object *o = data;
556 	pw_proxy_destroy(o->this.proxy);
557 }
558 
559 static void
destroy_proxy(void * data)560 destroy_proxy(void *data)
561 {
562 	struct object *o = data;
563 
564 	spa_assert(o->info);
565 
566 	if (o->info->events)
567 		spa_hook_remove(&o->object_listener);
568 	spa_hook_remove(&o->proxy_listener);
569 
570 	if (o->info->destroy)
571                 o->info->destroy(o);
572 
573         o->this.proxy = NULL;
574 }
575 
576 static const struct pw_proxy_events proxy_events = {
577         PW_VERSION_PROXY_EVENTS,
578         .removed = destroy_removed,
579         .destroy = destroy_proxy,
580 };
581 
registry_event_global(void * data,uint32_t id,uint32_t permissions,const char * type,uint32_t version,const struct spa_dict * props)582 static void registry_event_global(void *data, uint32_t id,
583 			uint32_t permissions, const char *type, uint32_t version,
584 			const struct spa_dict *props)
585 {
586 	struct manager *m = data;
587 	struct object *o;
588 	const struct object_info *info;
589 	struct pw_proxy *proxy;
590 
591 	info = find_info(type, version);
592 	if (info == NULL)
593 		return;
594 
595 	proxy = pw_registry_bind(m->this.registry,
596 			id, type, info->version, 0);
597         if (proxy == NULL)
598 		return;
599 
600 	o = calloc(1, sizeof(*o));
601 	if (o == NULL) {
602 		pw_log_error("can't alloc object for %u %s/%d: %m", id, type, version);
603 		pw_proxy_destroy(proxy);
604 		return;
605 	}
606 	o->this.id = id;
607 	o->this.permissions = permissions;
608 	o->this.type = info->type;
609 	o->this.version = version;
610 	o->this.props = props ? pw_properties_new_dict(props) : NULL;
611 	o->this.proxy = proxy;
612 	o->this.creating = true;
613 	spa_list_init(&o->this.param_list);
614 	spa_list_init(&o->pending_list);
615 	spa_list_init(&o->data_list);
616 
617 	o->manager = m;
618 	o->info = info;
619 	spa_list_append(&m->this.object_list, &o->this.link);
620 	m->this.n_objects++;
621 
622 	if (info->events)
623 		pw_proxy_add_object_listener(proxy,
624 				&o->object_listener,
625 				o->info->events, o);
626 	pw_proxy_add_listener(proxy,
627 			&o->proxy_listener,
628 			&proxy_events, o);
629 
630 	if (info->init)
631 		info->init(o);
632 
633 	core_sync(m);
634 }
635 
registry_event_global_remove(void * object,uint32_t id)636 static void registry_event_global_remove(void *object, uint32_t id)
637 {
638 	struct manager *m = object;
639 	struct object *o;
640 
641 	if ((o = find_object(m, id)) == NULL)
642 		return;
643 
644 	o->this.removing = true;
645 
646 	if (!o->this.creating)
647 		manager_emit_removed(m, &o->this);
648 
649 	object_destroy(o);
650 }
651 
652 static const struct pw_registry_events registry_events = {
653 	PW_VERSION_REGISTRY_EVENTS,
654 	.global = registry_event_global,
655 	.global_remove = registry_event_global_remove,
656 };
657 
on_core_info(void * data,const struct pw_core_info * info)658 static void on_core_info(void *data, const struct pw_core_info *info)
659 {
660 	struct manager *m = data;
661 	m->this.info = pw_core_info_merge(m->this.info, info, true);
662 }
663 
on_core_done(void * data,uint32_t id,int seq)664 static void on_core_done(void *data, uint32_t id, int seq)
665 {
666 	struct manager *m = data;
667 	struct object *o;
668 
669 	if (id == PW_ID_CORE) {
670 		if (m->sync_seq != seq)
671 			return;
672 
673 		pw_log_debug("sync end %u/%u", m->sync_seq, seq);
674 
675 		manager_emit_sync(m);
676 
677 		spa_list_for_each(o, &m->this.object_list, this.link)
678 			object_update_params(o);
679 
680 		spa_list_for_each(o, &m->this.object_list, this.link) {
681 			if (o->this.creating) {
682 				o->this.creating = false;
683 				manager_emit_added(m, &o->this);
684 				o->this.changed = 0;
685 			} else if (o->this.changed > 0) {
686 				manager_emit_updated(m, &o->this);
687 				o->this.changed = 0;
688 			}
689 		}
690 	}
691 }
692 
on_core_error(void * data,uint32_t id,int seq,int res,const char * message)693 static void on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
694 {
695 	struct manager *m = data;
696 
697 	if (id == PW_ID_CORE && res == -EPIPE) {
698 		pw_log_debug("connection error: %d, %s", res, message);
699 		manager_emit_disconnect(m);
700 	}
701 }
702 
703 static const struct pw_core_events core_events = {
704 	PW_VERSION_CORE_EVENTS,
705 	.done = on_core_done,
706 	.info = on_core_info,
707 	.error = on_core_error
708 };
709 
pw_manager_new(struct pw_core * core)710 struct pw_manager *pw_manager_new(struct pw_core *core)
711 {
712 	struct manager *m;
713 
714 	m = calloc(1, sizeof(*m));
715 	if (m == NULL)
716 		return NULL;
717 
718 	m->this.core = core;
719 	m->this.registry = pw_core_get_registry(m->this.core,
720 			PW_VERSION_REGISTRY, 0);
721 	if (m->this.registry == NULL) {
722 		free(m);
723 		return NULL;
724 	}
725 
726 	spa_hook_list_init(&m->hooks);
727 
728 	spa_list_init(&m->this.object_list);
729 
730 	pw_core_add_listener(m->this.core,
731 			&m->core_listener,
732 			&core_events, m);
733 	pw_registry_add_listener(m->this.registry,
734 			&m->registry_listener,
735 			&registry_events, m);
736 
737 	return &m->this;
738 }
739 
pw_manager_add_listener(struct pw_manager * manager,struct spa_hook * listener,const struct pw_manager_events * events,void * data)740 void pw_manager_add_listener(struct pw_manager *manager,
741 		struct spa_hook *listener,
742 		const struct pw_manager_events *events, void *data)
743 {
744 	struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
745 	spa_hook_list_append(&m->hooks, listener, events, data);
746 	core_sync(m);
747 }
748 
pw_manager_set_metadata(struct pw_manager * manager,struct pw_manager_object * metadata,uint32_t subject,const char * key,const char * type,const char * format,...)749 int pw_manager_set_metadata(struct pw_manager *manager,
750 		struct pw_manager_object *metadata,
751 		uint32_t subject, const char *key, const char *type,
752 		const char *format, ...)
753 {
754 	struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
755 	struct object *s;
756 	va_list args;
757 	char buf[1024];
758 	char *value;
759 
760 	if ((s = find_object(m, subject)) == NULL)
761 		return -ENOENT;
762 	if (!SPA_FLAG_IS_SET(s->this.permissions, PW_PERM_M))
763 		return -EACCES;
764 
765 	if (metadata == NULL)
766 		return -ENOTSUP;
767 	if (!SPA_FLAG_IS_SET(metadata->permissions, PW_PERM_W|PW_PERM_X))
768 		return -EACCES;
769 	if (metadata->proxy == NULL)
770 		return -ENOENT;
771 
772 	if (type != NULL) {
773 		va_start(args, format);
774 		vsnprintf(buf, sizeof(buf), format, args);
775 		va_end(args);
776 		value = buf;
777 	} else {
778 		spa_assert(format == NULL);
779 		value = NULL;
780 	}
781 
782 	pw_metadata_set_property(metadata->proxy,
783 			subject, key, type, value);
784 	return 0;
785 }
786 
pw_manager_for_each_object(struct pw_manager * manager,int (* callback)(void * data,struct pw_manager_object * object),void * data)787 int pw_manager_for_each_object(struct pw_manager *manager,
788 		int (*callback) (void *data, struct pw_manager_object *object),
789 		void *data)
790 {
791 	struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
792 	struct object *o;
793 	int res;
794 
795 	spa_list_for_each(o, &m->this.object_list, this.link) {
796 		if (o->this.creating)
797 			continue;
798 		if ((res = callback(data, &o->this)) != 0)
799 			return res;
800 	}
801 	return 0;
802 }
803 
pw_manager_destroy(struct pw_manager * manager)804 void pw_manager_destroy(struct pw_manager *manager)
805 {
806 	struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
807 	struct object *o;
808 
809 	spa_hook_remove(&m->core_listener);
810 
811 	spa_list_consume(o, &m->this.object_list, this.link)
812 		object_destroy(o);
813 
814 	spa_hook_remove(&m->registry_listener);
815 	pw_proxy_destroy((struct pw_proxy*)m->this.registry);
816 
817 	if (m->this.info)
818 		pw_core_info_free(m->this.info);
819 
820 	free(m);
821 }
822 
object_find_data(struct object * o,const char * id)823 static struct object_data *object_find_data(struct object *o, const char *id)
824 {
825 	struct object_data *d;
826 	spa_list_for_each(d, &o->data_list, link) {
827 		if (spa_streq(d->id, id))
828 			return d;
829 	}
830 	return NULL;
831 }
832 
pw_manager_object_add_data(struct pw_manager_object * obj,const char * id,size_t size)833 void *pw_manager_object_add_data(struct pw_manager_object *obj, const char *id, size_t size)
834 {
835 	struct object *o = SPA_CONTAINER_OF(obj, struct object, this);
836 	struct object_data *d;
837 
838 	d = object_find_data(o, id);
839 	if (d != NULL) {
840 		if (d->size == size)
841 			goto done;
842 		spa_list_remove(&d->link);
843 		free(d);
844 	}
845 
846 	d = calloc(1, sizeof(struct object_data) + size);
847 	d->id = id;
848 	d->size = size;
849 
850 	spa_list_append(&o->data_list, &d->link);
851 
852 done:
853 	return SPA_PTROFF(d, sizeof(struct object_data), void);
854 }
855 
pw_manager_object_get_data(struct pw_manager_object * obj,const char * id)856 void *pw_manager_object_get_data(struct pw_manager_object *obj, const char *id)
857 {
858 	struct object *o = SPA_CONTAINER_OF(obj, struct object, this);
859 	struct object_data *d = object_find_data(o, id);
860 
861 	return d ? SPA_PTROFF(d, sizeof(*d), void) : NULL;
862 }
863 
pw_manager_sync(struct pw_manager * manager)864 int pw_manager_sync(struct pw_manager *manager)
865 {
866 	struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
867 	return core_sync(m);
868 }
869 
pw_manager_object_is_client(struct pw_manager_object * o)870 bool pw_manager_object_is_client(struct pw_manager_object *o)
871 {
872 	return spa_streq(o->type, PW_TYPE_INTERFACE_Client);
873 }
874 
pw_manager_object_is_module(struct pw_manager_object * o)875 bool pw_manager_object_is_module(struct pw_manager_object *o)
876 {
877 	return spa_streq(o->type, PW_TYPE_INTERFACE_Module);
878 }
879 
pw_manager_object_is_card(struct pw_manager_object * o)880 bool pw_manager_object_is_card(struct pw_manager_object *o)
881 {
882 	const char *str;
883 	return spa_streq(o->type, PW_TYPE_INTERFACE_Device) &&
884 		o->props != NULL &&
885 		(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
886 		spa_streq(str, "Audio/Device");
887 }
888 
pw_manager_object_is_sink(struct pw_manager_object * o)889 bool pw_manager_object_is_sink(struct pw_manager_object *o)
890 {
891 	const char *str;
892 	return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
893 		o->props != NULL &&
894 		(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
895 		(spa_streq(str, "Audio/Sink") || spa_streq(str, "Audio/Duplex"));
896 }
897 
pw_manager_object_is_source(struct pw_manager_object * o)898 bool pw_manager_object_is_source(struct pw_manager_object *o)
899 {
900 	const char *str;
901 	return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
902 		o->props != NULL &&
903 		(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
904 		(spa_streq(str, "Audio/Source") ||
905 		 spa_streq(str, "Audio/Duplex") ||
906 		 spa_streq(str, "Audio/Source/Virtual"));
907 }
908 
pw_manager_object_is_monitor(struct pw_manager_object * o)909 bool pw_manager_object_is_monitor(struct pw_manager_object *o)
910 {
911 	const char *str;
912 	return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
913 		o->props != NULL &&
914 		(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
915 		(spa_streq(str, "Audio/Sink"));
916 }
917 
pw_manager_object_is_virtual(struct pw_manager_object * o)918 bool pw_manager_object_is_virtual(struct pw_manager_object *o)
919 {
920 	const char *str;
921 	struct pw_node_info *info;
922 	return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
923 		(info = o->info) != NULL && info->props != NULL &&
924 		(str = spa_dict_lookup(info->props, PW_KEY_NODE_VIRTUAL)) != NULL &&
925 		pw_properties_parse_bool(str);
926 }
927 
pw_manager_object_is_source_or_monitor(struct pw_manager_object * o)928 bool pw_manager_object_is_source_or_monitor(struct pw_manager_object *o)
929 {
930 	return pw_manager_object_is_source(o) || pw_manager_object_is_monitor(o);
931 }
932 
pw_manager_object_is_sink_input(struct pw_manager_object * o)933 bool pw_manager_object_is_sink_input(struct pw_manager_object *o)
934 {
935 	const char *str;
936 	return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
937 		o->props != NULL &&
938 		(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
939 		spa_streq(str, "Stream/Output/Audio");
940 }
941 
pw_manager_object_is_source_output(struct pw_manager_object * o)942 bool pw_manager_object_is_source_output(struct pw_manager_object *o)
943 {
944 	const char *str;
945 	return spa_streq(o->type, PW_TYPE_INTERFACE_Node) &&
946 		o->props != NULL &&
947 		(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
948 		spa_streq(str, "Stream/Input/Audio");
949 }
950 
pw_manager_object_is_recordable(struct pw_manager_object * o)951 bool pw_manager_object_is_recordable(struct pw_manager_object *o)
952 {
953 	return pw_manager_object_is_source(o) || pw_manager_object_is_sink(o) || pw_manager_object_is_sink_input(o);
954 }
955 
pw_manager_object_is_link(struct pw_manager_object * o)956 bool pw_manager_object_is_link(struct pw_manager_object *o)
957 {
958 	return spa_streq(o->type, PW_TYPE_INTERFACE_Link);
959 }
960