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, ®istry->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(®istry->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, ®istry->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, ®istry->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