153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2004-2006 Lennart Poettering 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as 853a5a1b3Sopenharmony_ci published by the Free Software Foundation; either version 2.1 of the 953a5a1b3Sopenharmony_ci License, or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public 1753a5a1b3Sopenharmony_ci License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <stdio.h> 2553a5a1b3Sopenharmony_ci#include <stdlib.h> 2653a5a1b3Sopenharmony_ci#include <unistd.h> 2753a5a1b3Sopenharmony_ci 2853a5a1b3Sopenharmony_ci#include <avahi-client/client.h> 2953a5a1b3Sopenharmony_ci#include <avahi-client/publish.h> 3053a5a1b3Sopenharmony_ci#include <avahi-common/alternative.h> 3153a5a1b3Sopenharmony_ci#include <avahi-common/error.h> 3253a5a1b3Sopenharmony_ci#include <avahi-common/domain.h> 3353a5a1b3Sopenharmony_ci 3453a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3553a5a1b3Sopenharmony_ci#include <pulse/util.h> 3653a5a1b3Sopenharmony_ci#include <pulse/thread-mainloop.h> 3753a5a1b3Sopenharmony_ci 3853a5a1b3Sopenharmony_ci#include <pulsecore/parseaddr.h> 3953a5a1b3Sopenharmony_ci#include <pulsecore/sink.h> 4053a5a1b3Sopenharmony_ci#include <pulsecore/source.h> 4153a5a1b3Sopenharmony_ci#include <pulsecore/native-common.h> 4253a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 4353a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 4453a5a1b3Sopenharmony_ci#include <pulsecore/dynarray.h> 4553a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 4653a5a1b3Sopenharmony_ci#include <pulsecore/avahi-wrap.h> 4753a5a1b3Sopenharmony_ci#include <pulsecore/protocol-native.h> 4853a5a1b3Sopenharmony_ci 4953a5a1b3Sopenharmony_ci#ifdef HAVE_DBUS 5053a5a1b3Sopenharmony_ci#include <pulsecore/dbus-shared.h> 5153a5a1b3Sopenharmony_ci 5253a5a1b3Sopenharmony_ci#define HOSTNAME_DBUS_INTERFACE "org.freedesktop.hostname1" 5353a5a1b3Sopenharmony_ci#define HOSTNAME_DBUS_PATH "/org/freedesktop/hostname1" 5453a5a1b3Sopenharmony_ci#define HOSTNAME_DBUS_ICON_PROPERTY "IconName" 5553a5a1b3Sopenharmony_ci#endif 5653a5a1b3Sopenharmony_ci 5753a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering"); 5853a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher"); 5953a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 6053a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(true); 6153a5a1b3Sopenharmony_ci 6253a5a1b3Sopenharmony_ci#define SERVICE_TYPE_SINK "_pulse-sink._tcp" 6353a5a1b3Sopenharmony_ci#define SERVICE_TYPE_SOURCE "_pulse-source._tcp" 6453a5a1b3Sopenharmony_ci#define SERVICE_TYPE_SERVER "_pulse-server._tcp" 6553a5a1b3Sopenharmony_ci#define SERVICE_SUBTYPE_SINK_HARDWARE "_hardware._sub."SERVICE_TYPE_SINK 6653a5a1b3Sopenharmony_ci#define SERVICE_SUBTYPE_SINK_VIRTUAL "_virtual._sub."SERVICE_TYPE_SINK 6753a5a1b3Sopenharmony_ci#define SERVICE_SUBTYPE_SOURCE_HARDWARE "_hardware._sub."SERVICE_TYPE_SOURCE 6853a5a1b3Sopenharmony_ci#define SERVICE_SUBTYPE_SOURCE_VIRTUAL "_virtual._sub."SERVICE_TYPE_SOURCE 6953a5a1b3Sopenharmony_ci#define SERVICE_SUBTYPE_SOURCE_MONITOR "_monitor._sub."SERVICE_TYPE_SOURCE 7053a5a1b3Sopenharmony_ci#define SERVICE_SUBTYPE_SOURCE_NON_MONITOR "_non-monitor._sub."SERVICE_TYPE_SOURCE 7153a5a1b3Sopenharmony_ci 7253a5a1b3Sopenharmony_ci/* 7353a5a1b3Sopenharmony_ci * Note: Because the core avahi-client calls result in synchronous D-Bus 7453a5a1b3Sopenharmony_ci * communication, calling any of those functions in the PA mainloop context 7553a5a1b3Sopenharmony_ci * could lead to the mainloop being blocked for long periods. 7653a5a1b3Sopenharmony_ci * 7753a5a1b3Sopenharmony_ci * To avoid this, we create a threaded-mainloop for Avahi calls, and push all 7853a5a1b3Sopenharmony_ci * D-Bus communication into that thread. The thumb-rule for the split is: 7953a5a1b3Sopenharmony_ci * 8053a5a1b3Sopenharmony_ci * 1. If access to PA data structures is needed, use the PA mainloop context 8153a5a1b3Sopenharmony_ci * 8253a5a1b3Sopenharmony_ci * 2. If a (blocking) avahi-client call is needed, use the Avahi mainloop 8353a5a1b3Sopenharmony_ci * 8453a5a1b3Sopenharmony_ci * We do have message queue to pass messages from the Avahi mainloop to the PA 8553a5a1b3Sopenharmony_ci * mainloop. 8653a5a1b3Sopenharmony_ci */ 8753a5a1b3Sopenharmony_ci 8853a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 8953a5a1b3Sopenharmony_ci NULL 9053a5a1b3Sopenharmony_ci}; 9153a5a1b3Sopenharmony_ci 9253a5a1b3Sopenharmony_cistruct avahi_msg { 9353a5a1b3Sopenharmony_ci pa_msgobject parent; 9453a5a1b3Sopenharmony_ci}; 9553a5a1b3Sopenharmony_ci 9653a5a1b3Sopenharmony_citypedef struct avahi_msg avahi_msg; 9753a5a1b3Sopenharmony_ciPA_DEFINE_PRIVATE_CLASS(avahi_msg, pa_msgobject); 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_cienum { 10053a5a1b3Sopenharmony_ci AVAHI_MESSAGE_PUBLISH_ALL, 10153a5a1b3Sopenharmony_ci AVAHI_MESSAGE_SHUTDOWN_START, 10253a5a1b3Sopenharmony_ci AVAHI_MESSAGE_SHUTDOWN_COMPLETE, 10353a5a1b3Sopenharmony_ci}; 10453a5a1b3Sopenharmony_ci 10553a5a1b3Sopenharmony_cienum service_subtype { 10653a5a1b3Sopenharmony_ci SUBTYPE_HARDWARE, 10753a5a1b3Sopenharmony_ci SUBTYPE_VIRTUAL, 10853a5a1b3Sopenharmony_ci SUBTYPE_MONITOR 10953a5a1b3Sopenharmony_ci}; 11053a5a1b3Sopenharmony_ci 11153a5a1b3Sopenharmony_cistruct service { 11253a5a1b3Sopenharmony_ci void *key; 11353a5a1b3Sopenharmony_ci 11453a5a1b3Sopenharmony_ci struct userdata *userdata; 11553a5a1b3Sopenharmony_ci AvahiEntryGroup *entry_group; 11653a5a1b3Sopenharmony_ci char *service_name; 11753a5a1b3Sopenharmony_ci const char *service_type; 11853a5a1b3Sopenharmony_ci enum service_subtype subtype; 11953a5a1b3Sopenharmony_ci 12053a5a1b3Sopenharmony_ci char *name; 12153a5a1b3Sopenharmony_ci bool is_sink; 12253a5a1b3Sopenharmony_ci pa_sample_spec ss; 12353a5a1b3Sopenharmony_ci pa_channel_map cm; 12453a5a1b3Sopenharmony_ci pa_proplist *proplist; 12553a5a1b3Sopenharmony_ci}; 12653a5a1b3Sopenharmony_ci 12753a5a1b3Sopenharmony_cistruct userdata { 12853a5a1b3Sopenharmony_ci pa_thread_mq thread_mq; 12953a5a1b3Sopenharmony_ci pa_rtpoll *rtpoll; 13053a5a1b3Sopenharmony_ci avahi_msg *msg; 13153a5a1b3Sopenharmony_ci 13253a5a1b3Sopenharmony_ci pa_core *core; 13353a5a1b3Sopenharmony_ci pa_module *module; 13453a5a1b3Sopenharmony_ci pa_mainloop_api *api; 13553a5a1b3Sopenharmony_ci pa_threaded_mainloop *mainloop; 13653a5a1b3Sopenharmony_ci 13753a5a1b3Sopenharmony_ci AvahiPoll *avahi_poll; 13853a5a1b3Sopenharmony_ci AvahiClient *client; 13953a5a1b3Sopenharmony_ci 14053a5a1b3Sopenharmony_ci pa_hashmap *services; /* protect with mainloop lock */ 14153a5a1b3Sopenharmony_ci char *service_name; 14253a5a1b3Sopenharmony_ci char *icon_name; 14353a5a1b3Sopenharmony_ci 14453a5a1b3Sopenharmony_ci AvahiEntryGroup *main_entry_group; 14553a5a1b3Sopenharmony_ci 14653a5a1b3Sopenharmony_ci pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot; 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci pa_native_protocol *native; 14953a5a1b3Sopenharmony_ci 15053a5a1b3Sopenharmony_ci bool shutting_down; /* Used in the main thread. */ 15153a5a1b3Sopenharmony_ci bool client_freed; /* Used in the Avahi thread. */ 15253a5a1b3Sopenharmony_ci}; 15353a5a1b3Sopenharmony_ci 15453a5a1b3Sopenharmony_ci/* Runs in PA mainloop context */ 15553a5a1b3Sopenharmony_cistatic void get_service_data(struct service *s, pa_object *device) { 15653a5a1b3Sopenharmony_ci pa_assert(s); 15753a5a1b3Sopenharmony_ci 15853a5a1b3Sopenharmony_ci if (pa_sink_isinstance(device)) { 15953a5a1b3Sopenharmony_ci pa_sink *sink = PA_SINK(device); 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_ci s->is_sink = true; 16253a5a1b3Sopenharmony_ci s->service_type = SERVICE_TYPE_SINK; 16353a5a1b3Sopenharmony_ci s->ss = sink->sample_spec; 16453a5a1b3Sopenharmony_ci s->cm = sink->channel_map; 16553a5a1b3Sopenharmony_ci s->name = pa_xstrdup(sink->name); 16653a5a1b3Sopenharmony_ci s->proplist = pa_proplist_copy(sink->proplist); 16753a5a1b3Sopenharmony_ci s->subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL; 16853a5a1b3Sopenharmony_ci 16953a5a1b3Sopenharmony_ci } else if (pa_source_isinstance(device)) { 17053a5a1b3Sopenharmony_ci pa_source *source = PA_SOURCE(device); 17153a5a1b3Sopenharmony_ci 17253a5a1b3Sopenharmony_ci s->is_sink = false; 17353a5a1b3Sopenharmony_ci s->service_type = SERVICE_TYPE_SOURCE; 17453a5a1b3Sopenharmony_ci s->ss = source->sample_spec; 17553a5a1b3Sopenharmony_ci s->cm = source->channel_map; 17653a5a1b3Sopenharmony_ci s->name = pa_xstrdup(source->name); 17753a5a1b3Sopenharmony_ci s->proplist = pa_proplist_copy(source->proplist); 17853a5a1b3Sopenharmony_ci s->subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL); 17953a5a1b3Sopenharmony_ci 18053a5a1b3Sopenharmony_ci } else 18153a5a1b3Sopenharmony_ci pa_assert_not_reached(); 18253a5a1b3Sopenharmony_ci} 18353a5a1b3Sopenharmony_ci 18453a5a1b3Sopenharmony_ci/* Can be used in either PA or Avahi mainloop context since the bits of u->core 18553a5a1b3Sopenharmony_ci * that we access don't change after startup. */ 18653a5a1b3Sopenharmony_cistatic AvahiStringList* txt_record_server_data(pa_core *c, AvahiStringList *l) { 18753a5a1b3Sopenharmony_ci char s[128]; 18853a5a1b3Sopenharmony_ci char *t; 18953a5a1b3Sopenharmony_ci 19053a5a1b3Sopenharmony_ci pa_assert(c); 19153a5a1b3Sopenharmony_ci 19253a5a1b3Sopenharmony_ci l = avahi_string_list_add_pair(l, "server-version", PACKAGE_NAME" "PACKAGE_VERSION); 19353a5a1b3Sopenharmony_ci 19453a5a1b3Sopenharmony_ci t = pa_get_user_name_malloc(); 19553a5a1b3Sopenharmony_ci l = avahi_string_list_add_pair(l, "user-name", t); 19653a5a1b3Sopenharmony_ci pa_xfree(t); 19753a5a1b3Sopenharmony_ci 19853a5a1b3Sopenharmony_ci t = pa_machine_id(); 19953a5a1b3Sopenharmony_ci l = avahi_string_list_add_pair(l, "machine-id", t); 20053a5a1b3Sopenharmony_ci pa_xfree(t); 20153a5a1b3Sopenharmony_ci 20253a5a1b3Sopenharmony_ci t = pa_uname_string(); 20353a5a1b3Sopenharmony_ci l = avahi_string_list_add_pair(l, "uname", t); 20453a5a1b3Sopenharmony_ci pa_xfree(t); 20553a5a1b3Sopenharmony_ci 20653a5a1b3Sopenharmony_ci l = avahi_string_list_add_pair(l, "fqdn", pa_get_fqdn(s, sizeof(s))); 20753a5a1b3Sopenharmony_ci l = avahi_string_list_add_printf(l, "cookie=0x%08x", c->cookie); 20853a5a1b3Sopenharmony_ci 20953a5a1b3Sopenharmony_ci return l; 21053a5a1b3Sopenharmony_ci} 21153a5a1b3Sopenharmony_ci 21253a5a1b3Sopenharmony_cistatic void publish_service(pa_mainloop_api *api, void *service); 21353a5a1b3Sopenharmony_ci 21453a5a1b3Sopenharmony_ci/* Runs in Avahi mainloop context */ 21553a5a1b3Sopenharmony_cistatic void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { 21653a5a1b3Sopenharmony_ci struct service *s = userdata; 21753a5a1b3Sopenharmony_ci 21853a5a1b3Sopenharmony_ci pa_assert(s); 21953a5a1b3Sopenharmony_ci 22053a5a1b3Sopenharmony_ci switch (state) { 22153a5a1b3Sopenharmony_ci 22253a5a1b3Sopenharmony_ci case AVAHI_ENTRY_GROUP_ESTABLISHED: 22353a5a1b3Sopenharmony_ci pa_log_info("Successfully established service %s.", s->service_name); 22453a5a1b3Sopenharmony_ci break; 22553a5a1b3Sopenharmony_ci 22653a5a1b3Sopenharmony_ci case AVAHI_ENTRY_GROUP_COLLISION: { 22753a5a1b3Sopenharmony_ci char *t; 22853a5a1b3Sopenharmony_ci 22953a5a1b3Sopenharmony_ci t = avahi_alternative_service_name(s->service_name); 23053a5a1b3Sopenharmony_ci pa_log_info("Name collision, renaming %s to %s.", s->service_name, t); 23153a5a1b3Sopenharmony_ci pa_xfree(s->service_name); 23253a5a1b3Sopenharmony_ci s->service_name = t; 23353a5a1b3Sopenharmony_ci 23453a5a1b3Sopenharmony_ci publish_service(NULL, s); 23553a5a1b3Sopenharmony_ci break; 23653a5a1b3Sopenharmony_ci } 23753a5a1b3Sopenharmony_ci 23853a5a1b3Sopenharmony_ci case AVAHI_ENTRY_GROUP_FAILURE: { 23953a5a1b3Sopenharmony_ci pa_log("Failed to register service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g)))); 24053a5a1b3Sopenharmony_ci 24153a5a1b3Sopenharmony_ci avahi_entry_group_free(g); 24253a5a1b3Sopenharmony_ci s->entry_group = NULL; 24353a5a1b3Sopenharmony_ci 24453a5a1b3Sopenharmony_ci break; 24553a5a1b3Sopenharmony_ci } 24653a5a1b3Sopenharmony_ci 24753a5a1b3Sopenharmony_ci case AVAHI_ENTRY_GROUP_UNCOMMITED: 24853a5a1b3Sopenharmony_ci case AVAHI_ENTRY_GROUP_REGISTERING: 24953a5a1b3Sopenharmony_ci ; 25053a5a1b3Sopenharmony_ci } 25153a5a1b3Sopenharmony_ci} 25253a5a1b3Sopenharmony_ci 25353a5a1b3Sopenharmony_cistatic void service_free(struct service *s); 25453a5a1b3Sopenharmony_ci 25553a5a1b3Sopenharmony_ci/* Can run in either context */ 25653a5a1b3Sopenharmony_cistatic uint16_t compute_port(struct userdata *u) { 25753a5a1b3Sopenharmony_ci pa_strlist *i; 25853a5a1b3Sopenharmony_ci 25953a5a1b3Sopenharmony_ci pa_assert(u); 26053a5a1b3Sopenharmony_ci 26153a5a1b3Sopenharmony_ci for (i = pa_native_protocol_servers(u->native); i; i = pa_strlist_next(i)) { 26253a5a1b3Sopenharmony_ci pa_parsed_address a; 26353a5a1b3Sopenharmony_ci 26453a5a1b3Sopenharmony_ci if (pa_parse_address(pa_strlist_data(i), &a) >= 0 && 26553a5a1b3Sopenharmony_ci (a.type == PA_PARSED_ADDRESS_TCP4 || 26653a5a1b3Sopenharmony_ci a.type == PA_PARSED_ADDRESS_TCP6 || 26753a5a1b3Sopenharmony_ci a.type == PA_PARSED_ADDRESS_TCP_AUTO) && 26853a5a1b3Sopenharmony_ci a.port > 0) { 26953a5a1b3Sopenharmony_ci 27053a5a1b3Sopenharmony_ci pa_xfree(a.path_or_host); 27153a5a1b3Sopenharmony_ci return a.port; 27253a5a1b3Sopenharmony_ci } 27353a5a1b3Sopenharmony_ci 27453a5a1b3Sopenharmony_ci pa_xfree(a.path_or_host); 27553a5a1b3Sopenharmony_ci } 27653a5a1b3Sopenharmony_ci 27753a5a1b3Sopenharmony_ci return PA_NATIVE_DEFAULT_PORT; 27853a5a1b3Sopenharmony_ci} 27953a5a1b3Sopenharmony_ci 28053a5a1b3Sopenharmony_ci/* Runs in Avahi mainloop context */ 28153a5a1b3Sopenharmony_cistatic void publish_service(pa_mainloop_api *api PA_GCC_UNUSED, void *service) { 28253a5a1b3Sopenharmony_ci struct service *s = (struct service *) service; 28353a5a1b3Sopenharmony_ci int r = -1; 28453a5a1b3Sopenharmony_ci AvahiStringList *txt = NULL; 28553a5a1b3Sopenharmony_ci char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; 28653a5a1b3Sopenharmony_ci const char *t; 28753a5a1b3Sopenharmony_ci 28853a5a1b3Sopenharmony_ci const char * const subtype_text[] = { 28953a5a1b3Sopenharmony_ci [SUBTYPE_HARDWARE] = "hardware", 29053a5a1b3Sopenharmony_ci [SUBTYPE_VIRTUAL] = "virtual", 29153a5a1b3Sopenharmony_ci [SUBTYPE_MONITOR] = "monitor" 29253a5a1b3Sopenharmony_ci }; 29353a5a1b3Sopenharmony_ci 29453a5a1b3Sopenharmony_ci pa_assert(s); 29553a5a1b3Sopenharmony_ci 29653a5a1b3Sopenharmony_ci if (!s->userdata->client || avahi_client_get_state(s->userdata->client) != AVAHI_CLIENT_S_RUNNING) 29753a5a1b3Sopenharmony_ci return; 29853a5a1b3Sopenharmony_ci 29953a5a1b3Sopenharmony_ci if (!s->entry_group) { 30053a5a1b3Sopenharmony_ci if (!(s->entry_group = avahi_entry_group_new(s->userdata->client, service_entry_group_callback, s))) { 30153a5a1b3Sopenharmony_ci pa_log("avahi_entry_group_new(): %s", avahi_strerror(avahi_client_errno(s->userdata->client))); 30253a5a1b3Sopenharmony_ci goto finish; 30353a5a1b3Sopenharmony_ci } 30453a5a1b3Sopenharmony_ci } else 30553a5a1b3Sopenharmony_ci avahi_entry_group_reset(s->entry_group); 30653a5a1b3Sopenharmony_ci 30753a5a1b3Sopenharmony_ci txt = txt_record_server_data(s->userdata->core, txt); 30853a5a1b3Sopenharmony_ci 30953a5a1b3Sopenharmony_ci txt = avahi_string_list_add_pair(txt, "device", s->name); 31053a5a1b3Sopenharmony_ci txt = avahi_string_list_add_printf(txt, "rate=%u", s->ss.rate); 31153a5a1b3Sopenharmony_ci txt = avahi_string_list_add_printf(txt, "channels=%u", s->ss.channels); 31253a5a1b3Sopenharmony_ci txt = avahi_string_list_add_pair(txt, "format", pa_sample_format_to_string(s->ss.format)); 31353a5a1b3Sopenharmony_ci txt = avahi_string_list_add_pair(txt, "channel_map", pa_channel_map_snprint(cm, sizeof(cm), &s->cm)); 31453a5a1b3Sopenharmony_ci txt = avahi_string_list_add_pair(txt, "subtype", subtype_text[s->subtype]); 31553a5a1b3Sopenharmony_ci 31653a5a1b3Sopenharmony_ci if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION))) 31753a5a1b3Sopenharmony_ci txt = avahi_string_list_add_pair(txt, "description", t); 31853a5a1b3Sopenharmony_ci if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_VENDOR_NAME))) 31953a5a1b3Sopenharmony_ci txt = avahi_string_list_add_pair(txt, "vendor-name", t); 32053a5a1b3Sopenharmony_ci if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_PRODUCT_NAME))) 32153a5a1b3Sopenharmony_ci txt = avahi_string_list_add_pair(txt, "product-name", t); 32253a5a1b3Sopenharmony_ci if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS))) 32353a5a1b3Sopenharmony_ci txt = avahi_string_list_add_pair(txt, "class", t); 32453a5a1b3Sopenharmony_ci if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_FORM_FACTOR))) 32553a5a1b3Sopenharmony_ci txt = avahi_string_list_add_pair(txt, "form-factor", t); 32653a5a1b3Sopenharmony_ci 32753a5a1b3Sopenharmony_ci if (s->userdata->icon_name) { 32853a5a1b3Sopenharmony_ci txt = avahi_string_list_add_pair(txt, "icon-name", s->userdata->icon_name); 32953a5a1b3Sopenharmony_ci } else if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_ICON_NAME))) { 33053a5a1b3Sopenharmony_ci txt = avahi_string_list_add_pair(txt, "icon-name", t); 33153a5a1b3Sopenharmony_ci } 33253a5a1b3Sopenharmony_ci 33353a5a1b3Sopenharmony_ci if (avahi_entry_group_add_service_strlst( 33453a5a1b3Sopenharmony_ci s->entry_group, 33553a5a1b3Sopenharmony_ci AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 33653a5a1b3Sopenharmony_ci 0, 33753a5a1b3Sopenharmony_ci s->service_name, 33853a5a1b3Sopenharmony_ci s->service_type, 33953a5a1b3Sopenharmony_ci NULL, 34053a5a1b3Sopenharmony_ci NULL, 34153a5a1b3Sopenharmony_ci compute_port(s->userdata), 34253a5a1b3Sopenharmony_ci txt) < 0) { 34353a5a1b3Sopenharmony_ci 34453a5a1b3Sopenharmony_ci pa_log("avahi_entry_group_add_service_strlst(): %s", avahi_strerror(avahi_client_errno(s->userdata->client))); 34553a5a1b3Sopenharmony_ci goto finish; 34653a5a1b3Sopenharmony_ci } 34753a5a1b3Sopenharmony_ci 34853a5a1b3Sopenharmony_ci if (avahi_entry_group_add_service_subtype( 34953a5a1b3Sopenharmony_ci s->entry_group, 35053a5a1b3Sopenharmony_ci AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 35153a5a1b3Sopenharmony_ci 0, 35253a5a1b3Sopenharmony_ci s->service_name, 35353a5a1b3Sopenharmony_ci s->service_type, 35453a5a1b3Sopenharmony_ci NULL, 35553a5a1b3Sopenharmony_ci s->is_sink ? (s->subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SINK_HARDWARE : SERVICE_SUBTYPE_SINK_VIRTUAL) : 35653a5a1b3Sopenharmony_ci (s->subtype == SUBTYPE_HARDWARE ? SERVICE_SUBTYPE_SOURCE_HARDWARE : (s->subtype == SUBTYPE_VIRTUAL ? SERVICE_SUBTYPE_SOURCE_VIRTUAL : SERVICE_SUBTYPE_SOURCE_MONITOR))) < 0) { 35753a5a1b3Sopenharmony_ci 35853a5a1b3Sopenharmony_ci pa_log("avahi_entry_group_add_service_subtype(): %s", avahi_strerror(avahi_client_errno(s->userdata->client))); 35953a5a1b3Sopenharmony_ci goto finish; 36053a5a1b3Sopenharmony_ci } 36153a5a1b3Sopenharmony_ci 36253a5a1b3Sopenharmony_ci if (!s->is_sink && s->subtype != SUBTYPE_MONITOR) { 36353a5a1b3Sopenharmony_ci if (avahi_entry_group_add_service_subtype( 36453a5a1b3Sopenharmony_ci s->entry_group, 36553a5a1b3Sopenharmony_ci AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 36653a5a1b3Sopenharmony_ci 0, 36753a5a1b3Sopenharmony_ci s->service_name, 36853a5a1b3Sopenharmony_ci SERVICE_TYPE_SOURCE, 36953a5a1b3Sopenharmony_ci NULL, 37053a5a1b3Sopenharmony_ci SERVICE_SUBTYPE_SOURCE_NON_MONITOR) < 0) { 37153a5a1b3Sopenharmony_ci 37253a5a1b3Sopenharmony_ci pa_log("avahi_entry_group_add_service_subtype(): %s", avahi_strerror(avahi_client_errno(s->userdata->client))); 37353a5a1b3Sopenharmony_ci goto finish; 37453a5a1b3Sopenharmony_ci } 37553a5a1b3Sopenharmony_ci } 37653a5a1b3Sopenharmony_ci 37753a5a1b3Sopenharmony_ci if (avahi_entry_group_commit(s->entry_group) < 0) { 37853a5a1b3Sopenharmony_ci pa_log("avahi_entry_group_commit(): %s", avahi_strerror(avahi_client_errno(s->userdata->client))); 37953a5a1b3Sopenharmony_ci goto finish; 38053a5a1b3Sopenharmony_ci } 38153a5a1b3Sopenharmony_ci 38253a5a1b3Sopenharmony_ci r = 0; 38353a5a1b3Sopenharmony_ci pa_log_debug("Successfully created entry group for %s.", s->service_name); 38453a5a1b3Sopenharmony_ci 38553a5a1b3Sopenharmony_cifinish: 38653a5a1b3Sopenharmony_ci 38753a5a1b3Sopenharmony_ci /* Remove this service */ 38853a5a1b3Sopenharmony_ci if (r < 0) 38953a5a1b3Sopenharmony_ci pa_hashmap_remove_and_free(s->userdata->services, s->key); 39053a5a1b3Sopenharmony_ci 39153a5a1b3Sopenharmony_ci avahi_string_list_free(txt); 39253a5a1b3Sopenharmony_ci} 39353a5a1b3Sopenharmony_ci 39453a5a1b3Sopenharmony_ci/* Runs in PA mainloop context */ 39553a5a1b3Sopenharmony_cistatic struct service *get_service(struct userdata *u, pa_object *device) { 39653a5a1b3Sopenharmony_ci struct service *s; 39753a5a1b3Sopenharmony_ci char *hn, *un; 39853a5a1b3Sopenharmony_ci const char *n; 39953a5a1b3Sopenharmony_ci 40053a5a1b3Sopenharmony_ci pa_assert(u); 40153a5a1b3Sopenharmony_ci pa_object_assert_ref(device); 40253a5a1b3Sopenharmony_ci 40353a5a1b3Sopenharmony_ci pa_threaded_mainloop_lock(u->mainloop); 40453a5a1b3Sopenharmony_ci 40553a5a1b3Sopenharmony_ci if ((s = pa_hashmap_get(u->services, device))) 40653a5a1b3Sopenharmony_ci goto out; 40753a5a1b3Sopenharmony_ci 40853a5a1b3Sopenharmony_ci s = pa_xnew(struct service, 1); 40953a5a1b3Sopenharmony_ci s->key = device; 41053a5a1b3Sopenharmony_ci s->userdata = u; 41153a5a1b3Sopenharmony_ci s->entry_group = NULL; 41253a5a1b3Sopenharmony_ci 41353a5a1b3Sopenharmony_ci get_service_data(s, device); 41453a5a1b3Sopenharmony_ci 41553a5a1b3Sopenharmony_ci if (!(n = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION))) 41653a5a1b3Sopenharmony_ci n = s->name; 41753a5a1b3Sopenharmony_ci 41853a5a1b3Sopenharmony_ci hn = pa_get_host_name_malloc(); 41953a5a1b3Sopenharmony_ci un = pa_get_user_name_malloc(); 42053a5a1b3Sopenharmony_ci 42153a5a1b3Sopenharmony_ci s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s", un, hn, n), AVAHI_LABEL_MAX-1); 42253a5a1b3Sopenharmony_ci 42353a5a1b3Sopenharmony_ci pa_xfree(un); 42453a5a1b3Sopenharmony_ci pa_xfree(hn); 42553a5a1b3Sopenharmony_ci 42653a5a1b3Sopenharmony_ci pa_hashmap_put(u->services, device, s); 42753a5a1b3Sopenharmony_ci 42853a5a1b3Sopenharmony_ciout: 42953a5a1b3Sopenharmony_ci pa_threaded_mainloop_unlock(u->mainloop); 43053a5a1b3Sopenharmony_ci 43153a5a1b3Sopenharmony_ci return s; 43253a5a1b3Sopenharmony_ci} 43353a5a1b3Sopenharmony_ci 43453a5a1b3Sopenharmony_ci/* Run from Avahi mainloop context */ 43553a5a1b3Sopenharmony_cistatic void service_free(struct service *s) { 43653a5a1b3Sopenharmony_ci pa_assert(s); 43753a5a1b3Sopenharmony_ci 43853a5a1b3Sopenharmony_ci if (s->entry_group) { 43953a5a1b3Sopenharmony_ci pa_log_debug("Removing entry group for %s.", s->service_name); 44053a5a1b3Sopenharmony_ci avahi_entry_group_free(s->entry_group); 44153a5a1b3Sopenharmony_ci } 44253a5a1b3Sopenharmony_ci 44353a5a1b3Sopenharmony_ci pa_xfree(s->service_name); 44453a5a1b3Sopenharmony_ci 44553a5a1b3Sopenharmony_ci pa_xfree(s->name); 44653a5a1b3Sopenharmony_ci pa_proplist_free(s->proplist); 44753a5a1b3Sopenharmony_ci 44853a5a1b3Sopenharmony_ci pa_xfree(s); 44953a5a1b3Sopenharmony_ci} 45053a5a1b3Sopenharmony_ci 45153a5a1b3Sopenharmony_ci/* Runs in PA mainloop context */ 45253a5a1b3Sopenharmony_cistatic bool shall_ignore(pa_object *o) { 45353a5a1b3Sopenharmony_ci pa_object_assert_ref(o); 45453a5a1b3Sopenharmony_ci 45553a5a1b3Sopenharmony_ci if (pa_sink_isinstance(o)) 45653a5a1b3Sopenharmony_ci return !!(PA_SINK(o)->flags & PA_SINK_NETWORK); 45753a5a1b3Sopenharmony_ci 45853a5a1b3Sopenharmony_ci if (pa_source_isinstance(o)) 45953a5a1b3Sopenharmony_ci return PA_SOURCE(o)->monitor_of || (PA_SOURCE(o)->flags & PA_SOURCE_NETWORK); 46053a5a1b3Sopenharmony_ci 46153a5a1b3Sopenharmony_ci pa_assert_not_reached(); 46253a5a1b3Sopenharmony_ci} 46353a5a1b3Sopenharmony_ci 46453a5a1b3Sopenharmony_ci/* Runs in PA mainloop context */ 46553a5a1b3Sopenharmony_cistatic pa_hook_result_t device_new_or_changed_cb(pa_core *c, pa_object *o, struct userdata *u) { 46653a5a1b3Sopenharmony_ci pa_assert(c); 46753a5a1b3Sopenharmony_ci pa_object_assert_ref(o); 46853a5a1b3Sopenharmony_ci 46953a5a1b3Sopenharmony_ci if (!shall_ignore(o)) { 47053a5a1b3Sopenharmony_ci pa_threaded_mainloop_lock(u->mainloop); 47153a5a1b3Sopenharmony_ci pa_mainloop_api_once(u->api, publish_service, get_service(u, o)); 47253a5a1b3Sopenharmony_ci pa_threaded_mainloop_unlock(u->mainloop); 47353a5a1b3Sopenharmony_ci } 47453a5a1b3Sopenharmony_ci 47553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 47653a5a1b3Sopenharmony_ci} 47753a5a1b3Sopenharmony_ci 47853a5a1b3Sopenharmony_ci/* Runs in PA mainloop context */ 47953a5a1b3Sopenharmony_cistatic pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) { 48053a5a1b3Sopenharmony_ci pa_assert(c); 48153a5a1b3Sopenharmony_ci pa_object_assert_ref(o); 48253a5a1b3Sopenharmony_ci 48353a5a1b3Sopenharmony_ci pa_threaded_mainloop_lock(u->mainloop); 48453a5a1b3Sopenharmony_ci pa_hashmap_remove_and_free(u->services, o); 48553a5a1b3Sopenharmony_ci pa_threaded_mainloop_unlock(u->mainloop); 48653a5a1b3Sopenharmony_ci 48753a5a1b3Sopenharmony_ci return PA_HOOK_OK; 48853a5a1b3Sopenharmony_ci} 48953a5a1b3Sopenharmony_ci 49053a5a1b3Sopenharmony_cistatic int publish_main_service(struct userdata *u); 49153a5a1b3Sopenharmony_ci 49253a5a1b3Sopenharmony_ci/* Runs in Avahi mainloop context */ 49353a5a1b3Sopenharmony_cistatic void main_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { 49453a5a1b3Sopenharmony_ci struct userdata *u = userdata; 49553a5a1b3Sopenharmony_ci pa_assert(u); 49653a5a1b3Sopenharmony_ci 49753a5a1b3Sopenharmony_ci switch (state) { 49853a5a1b3Sopenharmony_ci 49953a5a1b3Sopenharmony_ci case AVAHI_ENTRY_GROUP_ESTABLISHED: 50053a5a1b3Sopenharmony_ci pa_log_info("Successfully established main service."); 50153a5a1b3Sopenharmony_ci break; 50253a5a1b3Sopenharmony_ci 50353a5a1b3Sopenharmony_ci case AVAHI_ENTRY_GROUP_COLLISION: { 50453a5a1b3Sopenharmony_ci char *t; 50553a5a1b3Sopenharmony_ci 50653a5a1b3Sopenharmony_ci t = avahi_alternative_service_name(u->service_name); 50753a5a1b3Sopenharmony_ci pa_log_info("Name collision: renaming main service %s to %s.", u->service_name, t); 50853a5a1b3Sopenharmony_ci pa_xfree(u->service_name); 50953a5a1b3Sopenharmony_ci u->service_name = t; 51053a5a1b3Sopenharmony_ci 51153a5a1b3Sopenharmony_ci publish_main_service(u); 51253a5a1b3Sopenharmony_ci break; 51353a5a1b3Sopenharmony_ci } 51453a5a1b3Sopenharmony_ci 51553a5a1b3Sopenharmony_ci case AVAHI_ENTRY_GROUP_FAILURE: { 51653a5a1b3Sopenharmony_ci pa_log("Failed to register main service: %s", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g)))); 51753a5a1b3Sopenharmony_ci 51853a5a1b3Sopenharmony_ci avahi_entry_group_free(g); 51953a5a1b3Sopenharmony_ci u->main_entry_group = NULL; 52053a5a1b3Sopenharmony_ci break; 52153a5a1b3Sopenharmony_ci } 52253a5a1b3Sopenharmony_ci 52353a5a1b3Sopenharmony_ci case AVAHI_ENTRY_GROUP_UNCOMMITED: 52453a5a1b3Sopenharmony_ci case AVAHI_ENTRY_GROUP_REGISTERING: 52553a5a1b3Sopenharmony_ci break; 52653a5a1b3Sopenharmony_ci } 52753a5a1b3Sopenharmony_ci} 52853a5a1b3Sopenharmony_ci 52953a5a1b3Sopenharmony_ci/* Runs in Avahi mainloop context */ 53053a5a1b3Sopenharmony_cistatic int publish_main_service(struct userdata *u) { 53153a5a1b3Sopenharmony_ci AvahiStringList *txt = NULL; 53253a5a1b3Sopenharmony_ci int r = -1; 53353a5a1b3Sopenharmony_ci 53453a5a1b3Sopenharmony_ci pa_assert(u); 53553a5a1b3Sopenharmony_ci 53653a5a1b3Sopenharmony_ci if (!u->main_entry_group) { 53753a5a1b3Sopenharmony_ci if (!(u->main_entry_group = avahi_entry_group_new(u->client, main_entry_group_callback, u))) { 53853a5a1b3Sopenharmony_ci pa_log("avahi_entry_group_new() failed: %s", avahi_strerror(avahi_client_errno(u->client))); 53953a5a1b3Sopenharmony_ci goto fail; 54053a5a1b3Sopenharmony_ci } 54153a5a1b3Sopenharmony_ci } else 54253a5a1b3Sopenharmony_ci avahi_entry_group_reset(u->main_entry_group); 54353a5a1b3Sopenharmony_ci 54453a5a1b3Sopenharmony_ci txt = txt_record_server_data(u->core, txt); 54553a5a1b3Sopenharmony_ci 54653a5a1b3Sopenharmony_ci if (avahi_entry_group_add_service_strlst( 54753a5a1b3Sopenharmony_ci u->main_entry_group, 54853a5a1b3Sopenharmony_ci AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 54953a5a1b3Sopenharmony_ci 0, 55053a5a1b3Sopenharmony_ci u->service_name, 55153a5a1b3Sopenharmony_ci SERVICE_TYPE_SERVER, 55253a5a1b3Sopenharmony_ci NULL, 55353a5a1b3Sopenharmony_ci NULL, 55453a5a1b3Sopenharmony_ci compute_port(u), 55553a5a1b3Sopenharmony_ci txt) < 0) { 55653a5a1b3Sopenharmony_ci 55753a5a1b3Sopenharmony_ci pa_log("avahi_entry_group_add_service_strlst() failed: %s", avahi_strerror(avahi_client_errno(u->client))); 55853a5a1b3Sopenharmony_ci goto fail; 55953a5a1b3Sopenharmony_ci } 56053a5a1b3Sopenharmony_ci 56153a5a1b3Sopenharmony_ci if (avahi_entry_group_commit(u->main_entry_group) < 0) { 56253a5a1b3Sopenharmony_ci pa_log("avahi_entry_group_commit() failed: %s", avahi_strerror(avahi_client_errno(u->client))); 56353a5a1b3Sopenharmony_ci goto fail; 56453a5a1b3Sopenharmony_ci } 56553a5a1b3Sopenharmony_ci 56653a5a1b3Sopenharmony_ci r = 0; 56753a5a1b3Sopenharmony_ci 56853a5a1b3Sopenharmony_cifail: 56953a5a1b3Sopenharmony_ci avahi_string_list_free(txt); 57053a5a1b3Sopenharmony_ci 57153a5a1b3Sopenharmony_ci return r; 57253a5a1b3Sopenharmony_ci} 57353a5a1b3Sopenharmony_ci 57453a5a1b3Sopenharmony_ci/* Runs in PA mainloop context */ 57553a5a1b3Sopenharmony_cistatic int publish_all_services(struct userdata *u) { 57653a5a1b3Sopenharmony_ci pa_sink *sink; 57753a5a1b3Sopenharmony_ci pa_source *source; 57853a5a1b3Sopenharmony_ci int r = -1; 57953a5a1b3Sopenharmony_ci uint32_t idx; 58053a5a1b3Sopenharmony_ci 58153a5a1b3Sopenharmony_ci pa_assert(u); 58253a5a1b3Sopenharmony_ci 58353a5a1b3Sopenharmony_ci pa_log_debug("Publishing services in Zeroconf"); 58453a5a1b3Sopenharmony_ci 58553a5a1b3Sopenharmony_ci for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx))) 58653a5a1b3Sopenharmony_ci if (!shall_ignore(PA_OBJECT(sink))) { 58753a5a1b3Sopenharmony_ci pa_threaded_mainloop_lock(u->mainloop); 58853a5a1b3Sopenharmony_ci pa_mainloop_api_once(u->api, publish_service, get_service(u, PA_OBJECT(sink))); 58953a5a1b3Sopenharmony_ci pa_threaded_mainloop_unlock(u->mainloop); 59053a5a1b3Sopenharmony_ci } 59153a5a1b3Sopenharmony_ci 59253a5a1b3Sopenharmony_ci for (source = PA_SOURCE(pa_idxset_first(u->core->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(u->core->sources, &idx))) 59353a5a1b3Sopenharmony_ci if (!shall_ignore(PA_OBJECT(source))) { 59453a5a1b3Sopenharmony_ci pa_threaded_mainloop_lock(u->mainloop); 59553a5a1b3Sopenharmony_ci pa_mainloop_api_once(u->api, publish_service, get_service(u, PA_OBJECT(source))); 59653a5a1b3Sopenharmony_ci pa_threaded_mainloop_unlock(u->mainloop); 59753a5a1b3Sopenharmony_ci } 59853a5a1b3Sopenharmony_ci 59953a5a1b3Sopenharmony_ci if (publish_main_service(u) < 0) 60053a5a1b3Sopenharmony_ci goto fail; 60153a5a1b3Sopenharmony_ci 60253a5a1b3Sopenharmony_ci r = 0; 60353a5a1b3Sopenharmony_ci 60453a5a1b3Sopenharmony_cifail: 60553a5a1b3Sopenharmony_ci return r; 60653a5a1b3Sopenharmony_ci} 60753a5a1b3Sopenharmony_ci 60853a5a1b3Sopenharmony_ci/* Runs in Avahi mainloop context */ 60953a5a1b3Sopenharmony_cistatic void unpublish_all_services(struct userdata *u, bool rem) { 61053a5a1b3Sopenharmony_ci void *state = NULL; 61153a5a1b3Sopenharmony_ci struct service *s; 61253a5a1b3Sopenharmony_ci 61353a5a1b3Sopenharmony_ci pa_assert(u); 61453a5a1b3Sopenharmony_ci 61553a5a1b3Sopenharmony_ci pa_log_debug("Unpublishing services in Zeroconf"); 61653a5a1b3Sopenharmony_ci 61753a5a1b3Sopenharmony_ci while ((s = pa_hashmap_iterate(u->services, &state, NULL))) { 61853a5a1b3Sopenharmony_ci if (s->entry_group) { 61953a5a1b3Sopenharmony_ci if (rem) { 62053a5a1b3Sopenharmony_ci pa_log_debug("Removing entry group for %s.", s->service_name); 62153a5a1b3Sopenharmony_ci avahi_entry_group_free(s->entry_group); 62253a5a1b3Sopenharmony_ci s->entry_group = NULL; 62353a5a1b3Sopenharmony_ci } else { 62453a5a1b3Sopenharmony_ci avahi_entry_group_reset(s->entry_group); 62553a5a1b3Sopenharmony_ci pa_log_debug("Resetting entry group for %s.", s->service_name); 62653a5a1b3Sopenharmony_ci } 62753a5a1b3Sopenharmony_ci } 62853a5a1b3Sopenharmony_ci } 62953a5a1b3Sopenharmony_ci 63053a5a1b3Sopenharmony_ci if (u->main_entry_group) { 63153a5a1b3Sopenharmony_ci if (rem) { 63253a5a1b3Sopenharmony_ci pa_log_debug("Removing main entry group."); 63353a5a1b3Sopenharmony_ci avahi_entry_group_free(u->main_entry_group); 63453a5a1b3Sopenharmony_ci u->main_entry_group = NULL; 63553a5a1b3Sopenharmony_ci } else { 63653a5a1b3Sopenharmony_ci avahi_entry_group_reset(u->main_entry_group); 63753a5a1b3Sopenharmony_ci pa_log_debug("Resetting main entry group."); 63853a5a1b3Sopenharmony_ci } 63953a5a1b3Sopenharmony_ci } 64053a5a1b3Sopenharmony_ci} 64153a5a1b3Sopenharmony_ci 64253a5a1b3Sopenharmony_ci/* Runs in PA mainloop context */ 64353a5a1b3Sopenharmony_cistatic int avahi_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { 64453a5a1b3Sopenharmony_ci struct userdata *u = (struct userdata *) data; 64553a5a1b3Sopenharmony_ci 64653a5a1b3Sopenharmony_ci pa_assert(u); 64753a5a1b3Sopenharmony_ci 64853a5a1b3Sopenharmony_ci if (u->shutting_down || u->module->unload_requested) 64953a5a1b3Sopenharmony_ci return 0; 65053a5a1b3Sopenharmony_ci 65153a5a1b3Sopenharmony_ci switch (code) { 65253a5a1b3Sopenharmony_ci case AVAHI_MESSAGE_PUBLISH_ALL: 65353a5a1b3Sopenharmony_ci publish_all_services(u); 65453a5a1b3Sopenharmony_ci break; 65553a5a1b3Sopenharmony_ci 65653a5a1b3Sopenharmony_ci case AVAHI_MESSAGE_SHUTDOWN_START: 65753a5a1b3Sopenharmony_ci pa_module_unload(u->module, true); 65853a5a1b3Sopenharmony_ci break; 65953a5a1b3Sopenharmony_ci 66053a5a1b3Sopenharmony_ci default: 66153a5a1b3Sopenharmony_ci pa_assert_not_reached(); 66253a5a1b3Sopenharmony_ci } 66353a5a1b3Sopenharmony_ci 66453a5a1b3Sopenharmony_ci return 0; 66553a5a1b3Sopenharmony_ci} 66653a5a1b3Sopenharmony_ci 66753a5a1b3Sopenharmony_ci#ifdef HAVE_DBUS 66853a5a1b3Sopenharmony_cistatic char *get_icon_name(pa_module*m) { 66953a5a1b3Sopenharmony_ci const char *interface = HOSTNAME_DBUS_INTERFACE; 67053a5a1b3Sopenharmony_ci const char *property = HOSTNAME_DBUS_ICON_PROPERTY; 67153a5a1b3Sopenharmony_ci char *icon_name = NULL; 67253a5a1b3Sopenharmony_ci pa_dbus_connection *bus; 67353a5a1b3Sopenharmony_ci DBusError error; 67453a5a1b3Sopenharmony_ci DBusMessageIter args; 67553a5a1b3Sopenharmony_ci DBusMessage *msg = NULL; 67653a5a1b3Sopenharmony_ci DBusMessage *reply = NULL; 67753a5a1b3Sopenharmony_ci DBusConnection *conn = NULL; 67853a5a1b3Sopenharmony_ci DBusMessageIter sub; 67953a5a1b3Sopenharmony_ci 68053a5a1b3Sopenharmony_ci dbus_error_init(&error); 68153a5a1b3Sopenharmony_ci 68253a5a1b3Sopenharmony_ci if (!(bus = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error))) { 68353a5a1b3Sopenharmony_ci pa_log("Failed to get system bus connection: %s", error.message); 68453a5a1b3Sopenharmony_ci dbus_error_free(&error); 68553a5a1b3Sopenharmony_ci goto out; 68653a5a1b3Sopenharmony_ci } 68753a5a1b3Sopenharmony_ci 68853a5a1b3Sopenharmony_ci conn = pa_dbus_connection_get(bus); 68953a5a1b3Sopenharmony_ci 69053a5a1b3Sopenharmony_ci msg = dbus_message_new_method_call(HOSTNAME_DBUS_INTERFACE, 69153a5a1b3Sopenharmony_ci HOSTNAME_DBUS_PATH, 69253a5a1b3Sopenharmony_ci DBUS_INTERFACE_PROPERTIES, 69353a5a1b3Sopenharmony_ci "Get"); 69453a5a1b3Sopenharmony_ci dbus_message_append_args(msg, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID); 69553a5a1b3Sopenharmony_ci 69653a5a1b3Sopenharmony_ci if ((reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &error)) == NULL) { 69753a5a1b3Sopenharmony_ci pa_log("Failed to send: %s:%s", error.name, error.message); 69853a5a1b3Sopenharmony_ci dbus_error_free(&error); 69953a5a1b3Sopenharmony_ci goto out; 70053a5a1b3Sopenharmony_ci } 70153a5a1b3Sopenharmony_ci 70253a5a1b3Sopenharmony_ci dbus_message_iter_init(reply, &args); 70353a5a1b3Sopenharmony_ci if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_VARIANT) { 70453a5a1b3Sopenharmony_ci pa_log("Incorrect reply type"); 70553a5a1b3Sopenharmony_ci goto out; 70653a5a1b3Sopenharmony_ci } 70753a5a1b3Sopenharmony_ci 70853a5a1b3Sopenharmony_ci dbus_message_iter_recurse(&args, &sub); 70953a5a1b3Sopenharmony_ci 71053a5a1b3Sopenharmony_ci if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { 71153a5a1b3Sopenharmony_ci pa_log("Incorrect value type"); 71253a5a1b3Sopenharmony_ci goto out; 71353a5a1b3Sopenharmony_ci } 71453a5a1b3Sopenharmony_ci 71553a5a1b3Sopenharmony_ci dbus_message_iter_get_basic(&sub, &icon_name); 71653a5a1b3Sopenharmony_ci icon_name = pa_xstrdup(icon_name); 71753a5a1b3Sopenharmony_ci 71853a5a1b3Sopenharmony_ciout: 71953a5a1b3Sopenharmony_ci if (reply) 72053a5a1b3Sopenharmony_ci dbus_message_unref(reply); 72153a5a1b3Sopenharmony_ci 72253a5a1b3Sopenharmony_ci if (msg) 72353a5a1b3Sopenharmony_ci dbus_message_unref(msg); 72453a5a1b3Sopenharmony_ci 72553a5a1b3Sopenharmony_ci if (bus) 72653a5a1b3Sopenharmony_ci pa_dbus_connection_unref(bus); 72753a5a1b3Sopenharmony_ci 72853a5a1b3Sopenharmony_ci return icon_name; 72953a5a1b3Sopenharmony_ci} 73053a5a1b3Sopenharmony_ci#endif 73153a5a1b3Sopenharmony_ci 73253a5a1b3Sopenharmony_ci/* Runs in Avahi mainloop context */ 73353a5a1b3Sopenharmony_cistatic void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) { 73453a5a1b3Sopenharmony_ci struct userdata *u = userdata; 73553a5a1b3Sopenharmony_ci 73653a5a1b3Sopenharmony_ci pa_assert(c); 73753a5a1b3Sopenharmony_ci pa_assert(u); 73853a5a1b3Sopenharmony_ci 73953a5a1b3Sopenharmony_ci u->client = c; 74053a5a1b3Sopenharmony_ci 74153a5a1b3Sopenharmony_ci switch (state) { 74253a5a1b3Sopenharmony_ci case AVAHI_CLIENT_S_RUNNING: 74353a5a1b3Sopenharmony_ci /* Collect all sinks/sources, and publish them */ 74453a5a1b3Sopenharmony_ci pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_PUBLISH_ALL, u, 0, NULL, NULL); 74553a5a1b3Sopenharmony_ci 74653a5a1b3Sopenharmony_ci#ifdef HAVE_DBUS 74753a5a1b3Sopenharmony_ci /* Request icon name through D-BUS */ 74853a5a1b3Sopenharmony_ci u->icon_name = get_icon_name(u->module); 74953a5a1b3Sopenharmony_ci#endif 75053a5a1b3Sopenharmony_ci 75153a5a1b3Sopenharmony_ci break; 75253a5a1b3Sopenharmony_ci 75353a5a1b3Sopenharmony_ci case AVAHI_CLIENT_S_COLLISION: 75453a5a1b3Sopenharmony_ci pa_log_debug("Host name collision"); 75553a5a1b3Sopenharmony_ci unpublish_all_services(u, false); 75653a5a1b3Sopenharmony_ci break; 75753a5a1b3Sopenharmony_ci 75853a5a1b3Sopenharmony_ci case AVAHI_CLIENT_FAILURE: 75953a5a1b3Sopenharmony_ci if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) { 76053a5a1b3Sopenharmony_ci int error; 76153a5a1b3Sopenharmony_ci 76253a5a1b3Sopenharmony_ci pa_log_debug("Avahi daemon disconnected."); 76353a5a1b3Sopenharmony_ci 76453a5a1b3Sopenharmony_ci unpublish_all_services(u, true); 76553a5a1b3Sopenharmony_ci avahi_client_free(u->client); 76653a5a1b3Sopenharmony_ci 76753a5a1b3Sopenharmony_ci if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) { 76853a5a1b3Sopenharmony_ci pa_log("avahi_client_new() failed: %s", avahi_strerror(error)); 76953a5a1b3Sopenharmony_ci pa_module_unload_request(u->module, true); 77053a5a1b3Sopenharmony_ci } 77153a5a1b3Sopenharmony_ci } 77253a5a1b3Sopenharmony_ci 77353a5a1b3Sopenharmony_ci break; 77453a5a1b3Sopenharmony_ci 77553a5a1b3Sopenharmony_ci default: ; 77653a5a1b3Sopenharmony_ci } 77753a5a1b3Sopenharmony_ci} 77853a5a1b3Sopenharmony_ci 77953a5a1b3Sopenharmony_ci/* Runs in Avahi mainloop context */ 78053a5a1b3Sopenharmony_cistatic void create_client(pa_mainloop_api *api PA_GCC_UNUSED, void *userdata) { 78153a5a1b3Sopenharmony_ci struct userdata *u = (struct userdata *) userdata; 78253a5a1b3Sopenharmony_ci int error; 78353a5a1b3Sopenharmony_ci 78453a5a1b3Sopenharmony_ci /* create_client() and client_free() are called via defer events. If the 78553a5a1b3Sopenharmony_ci * two defer events are created very quickly one after another, we can't 78653a5a1b3Sopenharmony_ci * assume that the defer event that runs create_client() will be dispatched 78753a5a1b3Sopenharmony_ci * before the defer event that runs client_free() (at the time of writing, 78853a5a1b3Sopenharmony_ci * pa_mainloop actually always dispatches queued defer events in reverse 78953a5a1b3Sopenharmony_ci * creation order). For that reason we must be prepared for the case where 79053a5a1b3Sopenharmony_ci * client_free() has already been called. */ 79153a5a1b3Sopenharmony_ci if (u->client_freed) 79253a5a1b3Sopenharmony_ci return; 79353a5a1b3Sopenharmony_ci 79453a5a1b3Sopenharmony_ci pa_thread_mq_install(&u->thread_mq); 79553a5a1b3Sopenharmony_ci 79653a5a1b3Sopenharmony_ci if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) { 79753a5a1b3Sopenharmony_ci pa_log("avahi_client_new() failed: %s", avahi_strerror(error)); 79853a5a1b3Sopenharmony_ci goto fail; 79953a5a1b3Sopenharmony_ci } 80053a5a1b3Sopenharmony_ci 80153a5a1b3Sopenharmony_ci pa_log_debug("Started Avahi threaded mainloop"); 80253a5a1b3Sopenharmony_ci 80353a5a1b3Sopenharmony_ci return; 80453a5a1b3Sopenharmony_ci 80553a5a1b3Sopenharmony_cifail: 80653a5a1b3Sopenharmony_ci pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_SHUTDOWN_START, u, 0, NULL, NULL); 80753a5a1b3Sopenharmony_ci} 80853a5a1b3Sopenharmony_ci 80953a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 81053a5a1b3Sopenharmony_ci 81153a5a1b3Sopenharmony_ci struct userdata *u; 81253a5a1b3Sopenharmony_ci pa_modargs *ma = NULL; 81353a5a1b3Sopenharmony_ci char *hn, *un; 81453a5a1b3Sopenharmony_ci 81553a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 81653a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments."); 81753a5a1b3Sopenharmony_ci goto fail; 81853a5a1b3Sopenharmony_ci } 81953a5a1b3Sopenharmony_ci 82053a5a1b3Sopenharmony_ci m->userdata = u = pa_xnew0(struct userdata, 1); 82153a5a1b3Sopenharmony_ci u->core = m->core; 82253a5a1b3Sopenharmony_ci u->module = m; 82353a5a1b3Sopenharmony_ci u->native = pa_native_protocol_get(u->core); 82453a5a1b3Sopenharmony_ci 82553a5a1b3Sopenharmony_ci u->rtpoll = pa_rtpoll_new(); 82653a5a1b3Sopenharmony_ci u->mainloop = pa_threaded_mainloop_new(); 82753a5a1b3Sopenharmony_ci u->api = pa_threaded_mainloop_get_api(u->mainloop); 82853a5a1b3Sopenharmony_ci 82953a5a1b3Sopenharmony_ci if (pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll) < 0) { 83053a5a1b3Sopenharmony_ci pa_log("pa_thread_mq_init() failed."); 83153a5a1b3Sopenharmony_ci goto fail; 83253a5a1b3Sopenharmony_ci } 83353a5a1b3Sopenharmony_ci 83453a5a1b3Sopenharmony_ci u->msg = pa_msgobject_new(avahi_msg); 83553a5a1b3Sopenharmony_ci u->msg->parent.process_msg = avahi_process_msg; 83653a5a1b3Sopenharmony_ci 83753a5a1b3Sopenharmony_ci u->avahi_poll = pa_avahi_poll_new(u->api); 83853a5a1b3Sopenharmony_ci 83953a5a1b3Sopenharmony_ci u->services = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) service_free); 84053a5a1b3Sopenharmony_ci 84153a5a1b3Sopenharmony_ci u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); 84253a5a1b3Sopenharmony_ci u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); 84353a5a1b3Sopenharmony_ci u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u); 84453a5a1b3Sopenharmony_ci u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); 84553a5a1b3Sopenharmony_ci u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u); 84653a5a1b3Sopenharmony_ci u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u); 84753a5a1b3Sopenharmony_ci 84853a5a1b3Sopenharmony_ci un = pa_get_user_name_malloc(); 84953a5a1b3Sopenharmony_ci hn = pa_get_host_name_malloc(); 85053a5a1b3Sopenharmony_ci u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", un, hn), AVAHI_LABEL_MAX-1); 85153a5a1b3Sopenharmony_ci pa_xfree(un); 85253a5a1b3Sopenharmony_ci pa_xfree(hn); 85353a5a1b3Sopenharmony_ci 85453a5a1b3Sopenharmony_ci pa_threaded_mainloop_set_name(u->mainloop, "avahi-ml"); 85553a5a1b3Sopenharmony_ci if (pa_threaded_mainloop_start(u->mainloop) < 0) 85653a5a1b3Sopenharmony_ci goto fail; 85753a5a1b3Sopenharmony_ci 85853a5a1b3Sopenharmony_ci pa_threaded_mainloop_lock(u->mainloop); 85953a5a1b3Sopenharmony_ci pa_mainloop_api_once(u->api, create_client, u); 86053a5a1b3Sopenharmony_ci pa_threaded_mainloop_unlock(u->mainloop); 86153a5a1b3Sopenharmony_ci 86253a5a1b3Sopenharmony_ci pa_modargs_free(ma); 86353a5a1b3Sopenharmony_ci 86453a5a1b3Sopenharmony_ci return 0; 86553a5a1b3Sopenharmony_ci 86653a5a1b3Sopenharmony_cifail: 86753a5a1b3Sopenharmony_ci pa__done(m); 86853a5a1b3Sopenharmony_ci 86953a5a1b3Sopenharmony_ci if (ma) 87053a5a1b3Sopenharmony_ci pa_modargs_free(ma); 87153a5a1b3Sopenharmony_ci 87253a5a1b3Sopenharmony_ci return -1; 87353a5a1b3Sopenharmony_ci} 87453a5a1b3Sopenharmony_ci 87553a5a1b3Sopenharmony_ci/* Runs in Avahi mainloop context */ 87653a5a1b3Sopenharmony_cistatic void client_free(pa_mainloop_api *api PA_GCC_UNUSED, void *userdata) { 87753a5a1b3Sopenharmony_ci struct userdata *u = (struct userdata *) userdata; 87853a5a1b3Sopenharmony_ci 87953a5a1b3Sopenharmony_ci pa_hashmap_free(u->services); 88053a5a1b3Sopenharmony_ci 88153a5a1b3Sopenharmony_ci if (u->main_entry_group) 88253a5a1b3Sopenharmony_ci avahi_entry_group_free(u->main_entry_group); 88353a5a1b3Sopenharmony_ci 88453a5a1b3Sopenharmony_ci if (u->client) 88553a5a1b3Sopenharmony_ci avahi_client_free(u->client); 88653a5a1b3Sopenharmony_ci 88753a5a1b3Sopenharmony_ci if (u->avahi_poll) 88853a5a1b3Sopenharmony_ci pa_avahi_poll_free(u->avahi_poll); 88953a5a1b3Sopenharmony_ci 89053a5a1b3Sopenharmony_ci pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), AVAHI_MESSAGE_SHUTDOWN_COMPLETE, u, 0, NULL, NULL); 89153a5a1b3Sopenharmony_ci 89253a5a1b3Sopenharmony_ci u->client_freed = true; 89353a5a1b3Sopenharmony_ci} 89453a5a1b3Sopenharmony_ci 89553a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 89653a5a1b3Sopenharmony_ci struct userdata*u; 89753a5a1b3Sopenharmony_ci pa_assert(m); 89853a5a1b3Sopenharmony_ci 89953a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 90053a5a1b3Sopenharmony_ci return; 90153a5a1b3Sopenharmony_ci 90253a5a1b3Sopenharmony_ci u->shutting_down = true; 90353a5a1b3Sopenharmony_ci 90453a5a1b3Sopenharmony_ci pa_threaded_mainloop_lock(u->mainloop); 90553a5a1b3Sopenharmony_ci pa_mainloop_api_once(u->api, client_free, u); 90653a5a1b3Sopenharmony_ci pa_threaded_mainloop_unlock(u->mainloop); 90753a5a1b3Sopenharmony_ci pa_asyncmsgq_wait_for(u->thread_mq.outq, AVAHI_MESSAGE_SHUTDOWN_COMPLETE); 90853a5a1b3Sopenharmony_ci 90953a5a1b3Sopenharmony_ci pa_threaded_mainloop_stop(u->mainloop); 91053a5a1b3Sopenharmony_ci pa_threaded_mainloop_free(u->mainloop); 91153a5a1b3Sopenharmony_ci 91253a5a1b3Sopenharmony_ci pa_thread_mq_done(&u->thread_mq); 91353a5a1b3Sopenharmony_ci pa_rtpoll_free(u->rtpoll); 91453a5a1b3Sopenharmony_ci 91553a5a1b3Sopenharmony_ci if (u->sink_new_slot) 91653a5a1b3Sopenharmony_ci pa_hook_slot_free(u->sink_new_slot); 91753a5a1b3Sopenharmony_ci if (u->source_new_slot) 91853a5a1b3Sopenharmony_ci pa_hook_slot_free(u->source_new_slot); 91953a5a1b3Sopenharmony_ci if (u->sink_changed_slot) 92053a5a1b3Sopenharmony_ci pa_hook_slot_free(u->sink_changed_slot); 92153a5a1b3Sopenharmony_ci if (u->source_changed_slot) 92253a5a1b3Sopenharmony_ci pa_hook_slot_free(u->source_changed_slot); 92353a5a1b3Sopenharmony_ci if (u->sink_unlink_slot) 92453a5a1b3Sopenharmony_ci pa_hook_slot_free(u->sink_unlink_slot); 92553a5a1b3Sopenharmony_ci if (u->source_unlink_slot) 92653a5a1b3Sopenharmony_ci pa_hook_slot_free(u->source_unlink_slot); 92753a5a1b3Sopenharmony_ci 92853a5a1b3Sopenharmony_ci if (u->native) 92953a5a1b3Sopenharmony_ci pa_native_protocol_unref(u->native); 93053a5a1b3Sopenharmony_ci 93153a5a1b3Sopenharmony_ci pa_xfree(u->msg); 93253a5a1b3Sopenharmony_ci pa_xfree(u->service_name); 93353a5a1b3Sopenharmony_ci pa_xfree(u->icon_name); 93453a5a1b3Sopenharmony_ci pa_xfree(u); 93553a5a1b3Sopenharmony_ci} 936