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 "config.h"
26 
27 #include <unistd.h>
28 #include <limits.h>
29 #include <stdio.h>
30 #if !defined(__FreeBSD__) && !defined(__DragonFly__)
31 #include <sys/prctl.h>
32 #endif
33 #include <pwd.h>
34 #include <errno.h>
35 #include <dlfcn.h>
36 #include <pthread.h>
37 
38 #include <locale.h>
39 #include <libintl.h>
40 
41 #include <valgrind/valgrind.h>
42 
43 #include <spa/utils/names.h>
44 #include <spa/utils/string.h>
45 #include <spa/support/cpu.h>
46 #include <spa/support/i18n.h>
47 
48 #include "pipewire.h"
49 #include "private.h"
50 
51 #define MAX_SUPPORT	32
52 
53 #define SUPPORTLIB	"support/libspa-support"
54 
55 PW_LOG_TOPIC_EXTERN(log_context);
56 #define PW_LOG_TOPIC_DEFAULT log_context
57 
58 static char *prgname;
59 
60 static struct spa_i18n *_pipewire_i18n = NULL;
61 
62 struct plugin {
63 	struct spa_list link;
64 	char *filename;
65 	void *hnd;
66 	spa_handle_factory_enum_func_t enum_func;
67 	struct spa_list handles;
68 	int ref;
69 };
70 
71 struct handle {
72 	struct spa_list link;
73 	struct plugin *plugin;
74 	char *factory_name;
75 	int ref;
76 	struct spa_handle handle SPA_ALIGNED(8);
77 };
78 
79 struct registry {
80 	struct spa_list plugins;
81 };
82 
83 struct support {
84 	char **categories;
85 	const char *plugin_dir;
86 	const char *support_lib;
87 	struct registry registry;
88 	char *i18n_domain;
89 	struct spa_interface i18n_iface;
90 	struct spa_support support[MAX_SUPPORT];
91 	uint32_t n_support;
92 	unsigned int initialized:1;
93 	unsigned int in_valgrind:1;
94 	unsigned int no_color:1;
95 	unsigned int no_config:1;
96 };
97 
98 static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
99 static pthread_mutex_t support_lock = PTHREAD_MUTEX_INITIALIZER;
100 static struct support global_support;
101 
102 static struct plugin *
find_plugin(struct registry * registry,const char * filename)103 find_plugin(struct registry *registry, const char *filename)
104 {
105 	struct plugin *p;
106 	spa_list_for_each(p, &registry->plugins, link) {
107 		if (spa_streq(p->filename, filename))
108 			return p;
109 	}
110 	return NULL;
111 }
112 
113 static struct plugin *
open_plugin(struct registry * registry,const char * path,size_t len,const char * lib)114 open_plugin(struct registry *registry,
115 	    const char *path, size_t len, const char *lib)
116 {
117 	struct plugin *plugin;
118 	char filename[PATH_MAX];
119 	void *hnd;
120 	spa_handle_factory_enum_func_t enum_func;
121 	int res;
122 
123         if ((res = spa_scnprintf(filename, sizeof(filename), "%.*s/%s.so", (int)len, path, lib)) < 0)
124 		goto error_out;
125 
126 	if ((plugin = find_plugin(registry, filename)) != NULL) {
127 		plugin->ref++;
128 		return plugin;
129 	}
130 
131         if ((hnd = dlopen(filename, RTLD_NOW)) == NULL) {
132 		res = -ENOENT;
133 		pw_log_debug("can't load %s: %s", filename, dlerror());
134 		goto error_out;
135         }
136         if ((enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)) == NULL) {
137 		res = -ENOSYS;
138 		pw_log_debug("can't find enum function: %s", dlerror());
139 		goto error_dlclose;
140         }
141 
142 	if ((plugin = calloc(1, sizeof(struct plugin))) == NULL) {
143 		res = -errno;
144 		goto error_dlclose;
145 	}
146 
147 	pw_log_debug("loaded plugin:'%s'", filename);
148 	plugin->ref = 1;
149 	plugin->filename = strdup(filename);
150 	plugin->hnd = hnd;
151 	plugin->enum_func = enum_func;
152 	spa_list_init(&plugin->handles);
153 
154 	spa_list_append(&registry->plugins, &plugin->link);
155 
156 	return plugin;
157 
158 error_dlclose:
159 	dlclose(hnd);
160 error_out:
161 	errno = -res;
162 	return NULL;
163 }
164 
165 static void
unref_plugin(struct plugin * plugin)166 unref_plugin(struct plugin *plugin)
167 {
168 	if (--plugin->ref == 0) {
169 		spa_list_remove(&plugin->link);
170 		pw_log_debug("unloaded plugin:'%s'", plugin->filename);
171 		if (!global_support.in_valgrind)
172 			dlclose(plugin->hnd);
173 		free(plugin->filename);
174 		free(plugin);
175 	}
176 }
177 
find_factory(struct plugin * plugin,const char * factory_name)178 static const struct spa_handle_factory *find_factory(struct plugin *plugin, const char *factory_name)
179 {
180 	int res = -ENOENT;
181 	uint32_t index;
182         const struct spa_handle_factory *factory;
183 
184         for (index = 0;;) {
185                 if ((res = plugin->enum_func(&factory, &index)) <= 0) {
186                         if (res == 0)
187 				break;
188                         goto out;
189                 }
190 		if (factory->version < 1) {
191 			pw_log_warn("factory version %d < 1 not supported",
192 					factory->version);
193 			continue;
194 		}
195                 if (spa_streq(factory->name, factory_name))
196                         return factory;
197 	}
198 	res = -ENOENT;
199 out:
200 	pw_log_debug("can't find factory %s: %s", factory_name, spa_strerror(res));
201 	errno = -res;
202 	return NULL;
203 }
204 
unref_handle(struct handle * handle)205 static void unref_handle(struct handle *handle)
206 {
207 	if (--handle->ref == 0) {
208 		spa_list_remove(&handle->link);
209 		pw_log_debug("clear handle '%s'", handle->factory_name);
210 		pthread_mutex_unlock(&support_lock);
211 		spa_handle_clear(&handle->handle);
212 		pthread_mutex_lock(&support_lock);
213 		unref_plugin(handle->plugin);
214 		free(handle->factory_name);
215 		free(handle);
216 	}
217 }
218 
219 SPA_EXPORT
pw_get_support(struct spa_support * support,uint32_t max_support)220 uint32_t pw_get_support(struct spa_support *support, uint32_t max_support)
221 {
222 	uint32_t i, n = SPA_MIN(global_support.n_support, max_support);
223 	for (i = 0; i < n; i++)
224 		support[i] = global_support.support[i];
225 	return n;
226 }
227 
load_spa_handle(const char * lib,const char * factory_name,const struct spa_dict * info,uint32_t n_support,const struct spa_support support[])228 static struct spa_handle *load_spa_handle(const char *lib,
229 		const char *factory_name,
230 		const struct spa_dict *info,
231 		uint32_t n_support,
232 		const struct spa_support support[])
233 {
234 	struct support *sup = &global_support;
235 	struct plugin *plugin;
236 	struct handle *handle;
237 	const struct spa_handle_factory *factory;
238 	const char *state = NULL, *p;
239 	int res;
240 	size_t len;
241 
242 	if (factory_name == NULL) {
243 		res = -EINVAL;
244 		goto error_out;
245 	}
246 
247 	if (lib == NULL)
248 		lib = sup->support_lib;
249 
250 	pw_log_debug("load lib:'%s' factory-name:'%s'", lib, factory_name);
251 
252 	plugin = NULL;
253 	res = -ENOENT;
254 
255 	if (sup->plugin_dir == NULL) {
256 		pw_log_error("load lib: plugin directory undefined, set SPA_PLUGIN_DIR");
257 		goto error_out;
258 	}
259 	while ((p = pw_split_walk(sup->plugin_dir, ":", &len, &state))) {
260 		if ((plugin = open_plugin(&sup->registry, p, len, lib)) != NULL)
261 			break;
262 		res = -errno;
263 	}
264 	if (plugin == NULL)
265 		goto error_out;
266 
267 	pthread_mutex_unlock(&support_lock);
268 
269 	factory = find_factory(plugin, factory_name);
270 	if (factory == NULL) {
271 		res = -errno;
272 		goto error_unref_plugin;
273 	}
274 
275 	handle = calloc(1, sizeof(struct handle) + spa_handle_factory_get_size(factory, info));
276 	if (handle == NULL) {
277 		res = -errno;
278 		goto error_unref_plugin;
279 	}
280 
281 	if ((res = spa_handle_factory_init(factory,
282 					&handle->handle, info,
283 					support, n_support)) < 0) {
284 		pw_log_debug("can't make factory instance '%s': %d (%s)",
285 				factory_name, res, spa_strerror(res));
286 		goto error_free_handle;
287 	}
288 
289 	pthread_mutex_lock(&support_lock);
290 	handle->ref = 1;
291 	handle->plugin = plugin;
292 	handle->factory_name = strdup(factory_name);
293 	spa_list_append(&plugin->handles, &handle->link);
294 
295 	return &handle->handle;
296 
297 error_free_handle:
298 	free(handle);
299 error_unref_plugin:
300 	pthread_mutex_lock(&support_lock);
301 	unref_plugin(plugin);
302 error_out:
303 	errno = -res;
304 	return NULL;
305 }
306 
307 SPA_EXPORT
pw_load_spa_handle(const char * lib,const char * factory_name,const struct spa_dict * info,uint32_t n_support,const struct spa_support support[])308 struct spa_handle *pw_load_spa_handle(const char *lib,
309 		const char *factory_name,
310 		const struct spa_dict *info,
311 		uint32_t n_support,
312 		const struct spa_support support[])
313 {
314 	struct spa_handle *handle;
315 	pthread_mutex_lock(&support_lock);
316 	handle = load_spa_handle(lib, factory_name, info, n_support, support);
317 	pthread_mutex_unlock(&support_lock);
318 	return handle;
319 }
320 
find_handle(struct spa_handle * handle)321 static struct handle *find_handle(struct spa_handle *handle)
322 {
323 	struct registry *registry = &global_support.registry;
324 	struct plugin *p;
325 	struct handle *h;
326 
327 	spa_list_for_each(p, &registry->plugins, link) {
328 		spa_list_for_each(h, &p->handles, link) {
329 			if (&h->handle == handle)
330 				return h;
331 		}
332 	}
333 	return NULL;
334 }
335 
336 SPA_EXPORT
pw_unload_spa_handle(struct spa_handle * handle)337 int pw_unload_spa_handle(struct spa_handle *handle)
338 {
339 	struct handle *h;
340 	int res = 0;
341 
342 	pthread_mutex_lock(&support_lock);
343 	if ((h = find_handle(handle)) == NULL)
344 		res = -ENOENT;
345 	else
346 		unref_handle(h);
347 	pthread_mutex_unlock(&support_lock);
348 
349 	return res;
350 }
351 
add_interface(struct support * support,const char * factory_name,const char * type,const struct spa_dict * info)352 static void *add_interface(struct support *support,
353 		const char *factory_name,
354 		const char *type,
355 		const struct spa_dict *info)
356 {
357 	struct spa_handle *handle;
358 	void *iface = NULL;
359 	int res = -ENOENT;
360 
361 	handle = load_spa_handle(support->support_lib,
362 			factory_name, info,
363 			support->n_support, support->support);
364 	if (handle == NULL)
365 		return NULL;
366 
367 	pthread_mutex_unlock(&support_lock);
368 	res = spa_handle_get_interface(handle, type, &iface);
369 	pthread_mutex_lock(&support_lock);
370 
371 	if (res < 0 || iface == NULL) {
372 		pw_log_error("can't get %s interface %d: %s", type, res,
373 				spa_strerror(res));
374 		return NULL;
375 	}
376 
377 	support->support[support->n_support++] =
378 		SPA_SUPPORT_INIT(type, iface);
379 	return iface;
380 }
381 
382 SPA_EXPORT
pw_set_domain(const char * domain)383 int pw_set_domain(const char *domain)
384 {
385 	struct support *support = &global_support;
386 	free(support->i18n_domain);
387 	if (domain == NULL)
388 		support->i18n_domain = NULL;
389 	else if ((support->i18n_domain = strdup(domain)) == NULL)
390 		return -errno;
391 	return 0;
392 }
393 
394 SPA_EXPORT
pw_get_domain(void)395 const char *pw_get_domain(void)
396 {
397 	struct support *support = &global_support;
398 	return support->i18n_domain;
399 }
400 
i18n_text(void * object,const char * msgid)401 static const char *i18n_text(void *object, const char *msgid)
402 {
403 	struct support *support = object;
404 	return dgettext(support->i18n_domain, msgid);
405 }
406 
i18n_ntext(void * object,const char * msgid,const char * msgid_plural,unsigned long int n)407 static const char *i18n_ntext(void *object, const char *msgid, const char *msgid_plural,
408 		unsigned long int n)
409 {
410 	struct support *support = object;
411 	return dngettext(support->i18n_domain, msgid, msgid_plural, n);
412 }
413 
init_i18n(struct support * support)414 static void init_i18n(struct support *support)
415 {
416 	/* Load locale from the environment. */
417 	setlocale(LC_ALL, "");
418 	/* Set LC_NUMERIC to C so that floating point strings are consistently
419 	 * formatted and parsed across locales. */
420 	setlocale(LC_NUMERIC, "C");
421 	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
422 	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
423 	pw_set_domain(GETTEXT_PACKAGE);
424 }
425 
add_i18n(struct support * support)426 static void *add_i18n(struct support *support)
427 {
428 	static const struct spa_i18n_methods i18n_methods = {
429 		SPA_VERSION_I18N_METHODS,
430 		.text = i18n_text,
431 		.ntext = i18n_ntext,
432 	};
433 
434 	support->i18n_iface = SPA_INTERFACE_INIT(
435 			SPA_TYPE_INTERFACE_I18N,
436 			SPA_VERSION_I18N,
437 			&i18n_methods, support);
438 	_pipewire_i18n = (struct spa_i18n*) &support->i18n_iface;
439 
440 	support->support[support->n_support++] =
441 		SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_I18N, _pipewire_i18n);
442 
443 	return 0;
444 }
445 
446 SPA_EXPORT
pw_gettext(const char * msgid)447 const char *pw_gettext(const char *msgid)
448 {
449 	return spa_i18n_text(_pipewire_i18n, msgid);
450 }
451 SPA_EXPORT
pw_ngettext(const char * msgid,const char * msgid_plural,unsigned long int n)452 const char *pw_ngettext(const char *msgid, const char *msgid_plural, unsigned long int n)
453 {
454 	return spa_i18n_ntext(_pipewire_i18n, msgid, msgid_plural, n);
455 }
456 
457 #ifdef HAVE_SYSTEMD
load_journal_logger(struct support * support,const struct spa_dict * info)458 static struct spa_log *load_journal_logger(struct support *support,
459 					   const struct spa_dict *info)
460 {
461 	struct spa_handle *handle;
462 	void *iface = NULL;
463 	int res = -ENOENT;
464 	uint32_t i;
465 
466 	/* is the journal even available? */
467 	if (access("/run/systemd/journal/socket", F_OK) != 0)
468 		return NULL;
469 
470 	handle = load_spa_handle("support/libspa-journal",
471 				    SPA_NAME_SUPPORT_LOG, info,
472 				    support->n_support, support->support);
473 	if (handle == NULL)
474 		return NULL;
475 
476 	pthread_mutex_unlock(&support_lock);
477 	res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Log, &iface);
478 	pthread_mutex_lock(&support_lock);
479 
480 	if (res < 0 || iface == NULL) {
481 		pw_log_error("can't get log interface %d: %s", res,
482 				spa_strerror(res));
483 		return NULL;
484 	}
485 
486 	/* look for an existing logger, and
487 	 * replace it with the journal logger */
488 	for (i = 0; i < support->n_support; i++) {
489 		if (spa_streq(support->support[i].type, SPA_TYPE_INTERFACE_Log)) {
490 			support->support[i].data = iface;
491 			break;
492 		}
493 	}
494 	return (struct spa_log *) iface;
495 }
496 #endif
497 
498 static enum spa_log_level
parse_log_level(const char * str)499 parse_log_level(const char *str)
500 {
501 	enum spa_log_level l = SPA_LOG_LEVEL_NONE;
502 	switch(str[0]) {
503 		case 'X': l = SPA_LOG_LEVEL_NONE; break;
504 		case 'E': l = SPA_LOG_LEVEL_ERROR; break;
505 		case 'W': l = SPA_LOG_LEVEL_WARN; break;
506 		case 'I': l = SPA_LOG_LEVEL_INFO; break;
507 		case 'D': l = SPA_LOG_LEVEL_DEBUG; break;
508 		case 'T': l = SPA_LOG_LEVEL_TRACE; break;
509 		default:
510 			  l = atoi(str);
511 			  break;
512 	}
513 	return l;
514 }
515 
516 static char *
parse_pw_debug_env(void)517 parse_pw_debug_env(void)
518 {
519 	const char *str;
520 	char **tokens;
521 	int n_tokens;
522 	size_t slen;
523 	char json[1024] = {0};
524 	char *pos = json;
525 	char *end = pos + sizeof(json) - 1;
526 
527 	str = getenv("PIPEWIRE_DEBUG");
528 
529 	if (!str || (slen = strlen(str)) == 0)
530 		return NULL;
531 
532 	/* String format is PIPEWIRE_DEBUG=<glob>:<level>[,<glob>:<level>,...],
533 	 * converted into [{ conn.* = 0}, {glob = level}, {glob = level}, ....] ,
534 	 * with the connection namespace disabled by default.
535 	 */
536 	pos += spa_scnprintf(pos, end - pos, "[ { conn.* = %d },", SPA_LOG_LEVEL_NONE);
537 
538 	/* We only have single-digit log levels, so any single-character
539 	 * string is of the form PIPEWIRE_DEBUG=<N> */
540 	if (slen == 1) {
541 		pw_log_set_level(parse_log_level(str));
542 		goto out;
543 	}
544 
545 	tokens = pw_split_strv(str, ",", INT_MAX, &n_tokens);
546 	if (n_tokens > 0) {
547 		int i;
548 		for (i = 0; i < n_tokens; i++) {
549 			int n_tok;
550 			char **tok;
551 			char *pattern;
552 			enum spa_log_level lvl;
553 
554 			tok = pw_split_strv(tokens[i], ":", 2, &n_tok);
555 			if (n_tok == 2) {
556 				pattern = tok[0];
557 				lvl = parse_log_level(tok[1]);
558 
559 				pos += spa_scnprintf(pos, end - pos, "{ %s = %d },",
560 						     pattern, lvl);
561 			} else {
562 				pw_log_warn("Ignoring invalid format in PIPEWIRE_DEBUG: '%s'\n", tokens[i]);
563 			}
564 
565 			pw_free_strv(tok);
566 		}
567 	}
568 	pw_free_strv(tokens);
569 out:
570 	pos += spa_scnprintf(pos, end - pos, "]");
571 	return strdup(json);
572 }
573 
574 /** Initialize PipeWire
575  *
576  * \param argc pointer to argc
577  * \param argv pointer to argv
578  *
579  * Initialize the PipeWire system, parse and modify any parameters given
580  * by \a argc and \a argv and set up debugging.
581  *
582  * The environment variable \a PIPEWIRE_DEBUG
583  *
584  */
585 SPA_EXPORT
pipewire_init(int * argc,char ** argv[])586 void pipewire_init(int *argc, char **argv[])
587 {
588 	const char *str;
589 	struct spa_dict_item items[6];
590 	uint32_t n_items;
591 	struct spa_dict info;
592 	struct support *support = &global_support;
593 	struct spa_log *log;
594 	char level[32];
595 
596 	pthread_mutex_lock(&init_lock);
597 	if (support->initialized)
598 		goto done;
599 
600 	pthread_mutex_lock(&support_lock);
601 	support->in_valgrind = RUNNING_ON_VALGRIND;
602 
603 	if (getenv("NO_COLOR") != NULL)
604 		support->no_color = true;
605 
606 	if ((str = getenv("PIPEWIRE_NO_CONFIG")) != NULL)
607 		support->no_config = pw_properties_parse_bool(str);
608 
609 	init_i18n(support);
610 
611 	if ((str = getenv("SPA_PLUGIN_DIR")) == NULL)
612 		str = PLUGINDIR;
613 	support->plugin_dir = str;
614 
615 	if ((str = getenv("SPA_SUPPORT_LIB")) == NULL)
616 		str = SUPPORTLIB;
617 	support->support_lib = str;
618 
619 	spa_list_init(&support->registry.plugins);
620 
621 	if (pw_log_is_default()) {
622 		char *patterns = NULL;
623 
624 		n_items = 0;
625 		if (!support->no_color)
626 			items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_COLORS, "true");
627 		items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_TIMESTAMP, "true");
628 		if ((str = getenv("PIPEWIRE_LOG_LINE")) == NULL || spa_atob(str))
629 			items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_LINE, "true");
630 		snprintf(level, sizeof(level), "%d", pw_log_level);
631 		items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_LEVEL, level);
632 		if ((str = getenv("PIPEWIRE_LOG")) != NULL)
633 			items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_FILE, str);
634 		if ((patterns = parse_pw_debug_env()) != NULL)
635 			items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_PATTERNS, patterns);
636 		info = SPA_DICT_INIT(items, n_items);
637 
638 		log = add_interface(support, SPA_NAME_SUPPORT_LOG, SPA_TYPE_INTERFACE_Log, &info);
639 		if (log)
640 			pw_log_set(log);
641 
642 #ifdef HAVE_SYSTEMD
643 		if ((str = getenv("PIPEWIRE_LOG_SYSTEMD")) == NULL || spa_atob(str)) {
644 			log = load_journal_logger(support, &info);
645 			if (log)
646 				pw_log_set(log);
647 		}
648 #endif
649 		free(patterns);
650 	} else {
651 		support->support[support->n_support++] =
652 			SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Log, pw_log_get());
653 	}
654 
655 	pw_log_init();
656 
657 	n_items = 0;
658 	if ((str = getenv("PIPEWIRE_CPU")))
659 		items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_CPU_FORCE, str);
660 	if ((str = getenv("PIPEWIRE_VM")))
661 		items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_CPU_VM_TYPE, str);
662 	info = SPA_DICT_INIT(items, n_items);
663 
664 	add_interface(support, SPA_NAME_SUPPORT_CPU, SPA_TYPE_INTERFACE_CPU, &info);
665 
666 	add_i18n(support);
667 
668 	pw_log_info("version %s", pw_get_library_version());
669 	support->initialized = true;
670 	pthread_mutex_unlock(&support_lock);
671 done:
672 	pthread_mutex_unlock(&init_lock);
673 }
674 
675 
676 SPA_EXPORT
pipewire_deinit(void)677 void pipewire_deinit(void)
678 {
679 	struct support *support = &global_support;
680 	struct registry *registry = &support->registry;
681 	struct plugin *p;
682 
683 	pthread_mutex_lock(&init_lock);
684 	pthread_mutex_lock(&support_lock);
685 	pw_log_set(NULL);
686 	spa_list_consume(p, &registry->plugins, link) {
687 		struct handle *h;
688 		p->ref++;
689 		spa_list_consume(h, &p->handles, link)
690 			unref_handle(h);
691 		unref_plugin(p);
692 	}
693 	pw_free_strv(support->categories);
694 	free(support->i18n_domain);
695 	spa_zero(global_support);
696 	pthread_mutex_unlock(&support_lock);
697 	pthread_mutex_unlock(&init_lock);
698 
699 }
700 
701 #if !defined(__FreeBSD__)
702 #undef pw_init
703 SPA_EXPORT
pw_init(int * argc,char ** argv[])704 void pw_init(int *argc, char **argv[])
705 {
706 	pipewire_init(argc, argv);
707 }
708 #undef pw_deinit
709 SPA_EXPORT
pw_deinit(void)710 void pw_deinit(void)
711 {
712 	pipewire_deinit();
713 }
714 #endif
715 
716 /** Check if a debug category is enabled
717  *
718  * \param name the name of the category to check
719  * \return true if enabled
720  *
721  * Debugging categories can be enabled by using the PIPEWIRE_DEBUG
722  * environment variable
723  *
724  */
725 SPA_EXPORT
pw_debug_is_category_enabled(const char * name)726 bool pw_debug_is_category_enabled(const char *name)
727 {
728 	int i;
729 
730 	if (global_support.categories == NULL)
731 		return false;
732 
733 	for (i = 0; global_support.categories[i]; i++) {
734 		if (spa_streq(global_support.categories[i], name))
735 			return true;
736 	}
737 	return false;
738 }
739 
740 /** Get the application name */
741 SPA_EXPORT
pw_get_application_name(void)742 const char *pw_get_application_name(void)
743 {
744 	errno = ENOTSUP;
745 	return NULL;
746 }
747 
init_prgname(void)748 static void init_prgname(void)
749 {
750 	static char name[PATH_MAX];
751 
752 	spa_memzero(name, sizeof(name));
753 #if defined(__linux__) || defined(__FreeBSD_kernel__)
754 	{
755 		if (readlink("/proc/self/exe", name, sizeof(name)-1) > 0) {
756 			prgname = strrchr(name, '/') + 1;
757 			return;
758 		}
759 	}
760 #endif
761 #if defined(__FreeBSD__) || defined(__DragonFly__)
762 	{
763 		ssize_t len;
764 
765 		if ((len = readlink("/proc/curproc/file", name, sizeof(name)-1)) > 0) {
766 			prgname = strrchr(name, '/') + 1;
767 			return;
768 		}
769 	}
770 #endif
771 #if !defined(__FreeBSD__) && !defined(__DragonFly__)
772 	{
773 		if (prctl(PR_GET_NAME, (unsigned long) name, 0, 0, 0) == 0) {
774 			prgname = name;
775 			return;
776 		}
777 	}
778 #endif
779 	snprintf(name, sizeof(name), "pid-%d", getpid());
780 	prgname = name;
781 }
782 
783 /** Get the program name */
784 SPA_EXPORT
pw_get_prgname(void)785 const char *pw_get_prgname(void)
786 {
787 	static pthread_once_t prgname_is_initialized = PTHREAD_ONCE_INIT;
788 
789 	pthread_once(&prgname_is_initialized, init_prgname);
790 	return prgname;
791 }
792 
793 /** Get the user name */
794 SPA_EXPORT
pw_get_user_name(void)795 const char *pw_get_user_name(void)
796 {
797 	struct passwd *pw;
798 
799 	if ((pw = getpwuid(getuid())))
800 		return pw->pw_name;
801 
802 	return NULL;
803 }
804 
805 /** Get the host name */
806 SPA_EXPORT
pw_get_host_name(void)807 const char *pw_get_host_name(void)
808 {
809 	static char hname[256];
810 
811 	if (gethostname(hname, 256) < 0)
812 		return NULL;
813 
814 	hname[255] = 0;
815 	return hname;
816 }
817 
818 SPA_EXPORT
pw_in_valgrind(void)819 bool pw_in_valgrind(void)
820 {
821 	return global_support.in_valgrind;
822 }
823 
824 SPA_EXPORT
pw_check_option(const char * option,const char * value)825 bool pw_check_option(const char *option, const char *value)
826 {
827 	if (spa_streq(option, "in-valgrind"))
828 		return global_support.in_valgrind == spa_atob(value);
829 	else if (spa_streq(option, "no-color"))
830 		return global_support.no_color == spa_atob(value);
831 	else if (spa_streq(option, "no-config"))
832 		return global_support.no_config == spa_atob(value);
833 	else
834 		return false;
835 }
836 
837 /** Get the client name
838  *
839  * Make a new PipeWire client name that can be used to construct a remote.
840  *
841  */
842 SPA_EXPORT
pw_get_client_name(void)843 const char *pw_get_client_name(void)
844 {
845 	const char *cc;
846 	static char cname[256];
847 
848 	if ((cc = pw_get_application_name()))
849 		return cc;
850 	else if ((cc = pw_get_prgname()))
851 		return cc;
852 	else {
853 		if (snprintf(cname, sizeof(cname), "pipewire-pid-%zd", (size_t) getpid()) < 0)
854 			return NULL;
855 		return cname;
856 	}
857 }
858 
859 /** Reverse the direction */
860 SPA_EXPORT
pw_direction_reverse(enum pw_direction direction)861 enum pw_direction pw_direction_reverse(enum pw_direction direction)
862 {
863 	if (direction == PW_DIRECTION_INPUT)
864 		return PW_DIRECTION_OUTPUT;
865 	else if (direction == PW_DIRECTION_OUTPUT)
866 		return PW_DIRECTION_INPUT;
867 	return direction;
868 }
869 
870 /** Get the currently running version */
871 SPA_EXPORT
pw_get_library_version(void)872 const char* pw_get_library_version(void)
873 {
874 	return pw_get_headers_version();
875 }
876 
877 static const struct spa_type_info type_info[] = {
878 	{ SPA_ID_INVALID, SPA_ID_INVALID, "spa_types", spa_types },
879 	{ 0, 0, NULL, NULL },
880 };
881 
882 SPA_EXPORT
pw_type_info(void)883 const struct spa_type_info * pw_type_info(void)
884 {
885 	return type_info;
886 }
887