mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-05 13:30:02 -05:00
pulse-server: module-zeroconf-publish: handle disconnection
Keep track of the created services in two lists: published, pending. Move services between the lists as the avahi client's state changes: keep services in the pending list until the avahi daemon appears on dbus, move them to the pending list if connection is lost, and re-publish them after reconnection.
This commit is contained in:
parent
987c7fc1e4
commit
422d69b471
1 changed files with 88 additions and 64 deletions
|
|
@ -66,6 +66,8 @@ enum service_subtype {
|
|||
};
|
||||
|
||||
struct service {
|
||||
struct spa_list link;
|
||||
|
||||
struct module_zeroconf_publish_data *userdata;
|
||||
AvahiEntryGroup *entry_group;
|
||||
const char *service_type;
|
||||
|
|
@ -94,7 +96,10 @@ struct module_zeroconf_publish_data {
|
|||
|
||||
AvahiPoll *avahi_poll;
|
||||
AvahiClient *client;
|
||||
bool entry_group_free;
|
||||
|
||||
/* lists of services */
|
||||
struct spa_list pending;
|
||||
struct spa_list published;
|
||||
};
|
||||
|
||||
static void on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
|
||||
|
|
@ -125,19 +130,8 @@ static void get_service_name(struct pw_manager_object *o, char *buf, size_t leng
|
|||
snprintf(buf, length, "%s@%s: %s", un, hn, n);
|
||||
}
|
||||
|
||||
static int service_free(void *d, struct pw_manager_object *o)
|
||||
static void service_free(struct service *s)
|
||||
{
|
||||
struct service *s;
|
||||
|
||||
if (!pw_manager_object_is_sink(o) && !pw_manager_object_is_source(o))
|
||||
return 0;
|
||||
|
||||
s = pw_manager_object_get_data(o, SERVICE_DATA_ID);
|
||||
if (s == NULL) {
|
||||
pw_log_error("Could not find service to remove");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s->entry_group) {
|
||||
pw_log_debug("Removing entry group for %s.", s->service_name);
|
||||
avahi_entry_group_free(s->entry_group);
|
||||
|
|
@ -149,42 +143,21 @@ static int service_free(void *d, struct pw_manager_object *o)
|
|||
}
|
||||
|
||||
pw_properties_free(s->props);
|
||||
|
||||
return 0;
|
||||
spa_list_remove(&s->link);
|
||||
}
|
||||
|
||||
static int unpublish_service(void *data, struct pw_manager_object *o)
|
||||
static void unpublish_service(struct service *s)
|
||||
{
|
||||
spa_list_remove(&s->link);
|
||||
spa_list_append(&s->userdata->pending, &s->link);
|
||||
}
|
||||
|
||||
static void unpublish_all_services(struct module_zeroconf_publish_data *d)
|
||||
{
|
||||
struct module_zeroconf_publish_data *d = data;
|
||||
struct service *s;
|
||||
|
||||
if (!pw_manager_object_is_sink(o) && !pw_manager_object_is_source(o))
|
||||
return 0;
|
||||
|
||||
s = pw_manager_object_get_data(o, SERVICE_DATA_ID);
|
||||
if (s == NULL) {
|
||||
pw_log_error("Could not find service to remove");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s->entry_group) {
|
||||
if (d->entry_group_free) {
|
||||
pw_log_debug("Removing entry group for %s.", s->service_name);
|
||||
avahi_entry_group_free(s->entry_group);
|
||||
s->entry_group = NULL;
|
||||
} else {
|
||||
avahi_entry_group_reset(s->entry_group);
|
||||
pw_log_debug("Resetting entry group for %s.", s->service_name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unpublish_all_services(struct module_zeroconf_publish_data *d, bool entry_group_free)
|
||||
{
|
||||
d->entry_group_free = entry_group_free;
|
||||
pw_manager_for_each_object(d->manager, unpublish_service, d);
|
||||
spa_list_consume(s, &d->published, link)
|
||||
unpublish_service(s);
|
||||
}
|
||||
|
||||
static char* channel_map_snprint(char *s, size_t l, const struct channel_map *map)
|
||||
|
|
@ -289,6 +262,7 @@ static struct service *create_service(struct module_zeroconf_publish_data *d, st
|
|||
s->userdata = d;
|
||||
s->entry_group = NULL;
|
||||
get_service_name(o, s->service_name, sizeof(s->service_name));
|
||||
spa_list_append(&d->pending, &s->link);
|
||||
|
||||
fill_service_data(d, s, o);
|
||||
|
||||
|
|
@ -321,6 +295,15 @@ static AvahiStringList* txt_record_server_data(struct pw_core_info *info, AvahiS
|
|||
return l;
|
||||
}
|
||||
|
||||
static void clear_entry_group(struct service *s)
|
||||
{
|
||||
if (s->entry_group == NULL)
|
||||
return;
|
||||
|
||||
avahi_entry_group_free(s->entry_group);
|
||||
s->entry_group = NULL;
|
||||
}
|
||||
|
||||
static void publish_service(struct service *s);
|
||||
|
||||
static void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata)
|
||||
|
|
@ -343,14 +326,16 @@ static void service_entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupStat
|
|||
snprintf(s->service_name, sizeof(s->service_name), "%s", t);
|
||||
avahi_free(t);
|
||||
|
||||
unpublish_service(s);
|
||||
publish_service(s);
|
||||
break;
|
||||
}
|
||||
case AVAHI_ENTRY_GROUP_FAILURE:
|
||||
pw_log_error("Failed to register service: %s",
|
||||
avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))));
|
||||
avahi_entry_group_free(g);
|
||||
s->entry_group = NULL;
|
||||
|
||||
unpublish_service(s);
|
||||
clear_entry_group(s);
|
||||
break;
|
||||
|
||||
case AVAHI_ENTRY_GROUP_UNCOMMITED:
|
||||
|
|
@ -379,14 +364,15 @@ static void publish_service(struct service *s)
|
|||
return;
|
||||
|
||||
if (!s->entry_group) {
|
||||
if (!(s->entry_group =
|
||||
avahi_entry_group_new(s->userdata->client, service_entry_group_callback, s))) {
|
||||
s->entry_group = avahi_entry_group_new(s->userdata->client, service_entry_group_callback, s);
|
||||
if (s->entry_group == NULL) {
|
||||
pw_log_error("avahi_entry_group_new(): %s",
|
||||
avahi_strerror(avahi_client_errno(s->userdata->client)));
|
||||
goto finish;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
avahi_entry_group_reset(s->entry_group);
|
||||
}
|
||||
|
||||
txt = txt_record_server_data(s->userdata->manager->info, txt);
|
||||
|
||||
|
|
@ -461,12 +447,31 @@ static void publish_service(struct service *s)
|
|||
goto finish;
|
||||
}
|
||||
|
||||
spa_list_remove(&s->link);
|
||||
spa_list_append(&s->userdata->published, &s->link);
|
||||
|
||||
pw_log_info("Successfully created entry group for %s.", s->service_name);
|
||||
|
||||
finish:
|
||||
avahi_string_list_free(txt);
|
||||
}
|
||||
|
||||
static void publish_pending(struct module_zeroconf_publish_data *data)
|
||||
{
|
||||
struct service *s, *next;
|
||||
|
||||
spa_list_for_each_safe(s, next, &data->pending, link)
|
||||
publish_service(s);
|
||||
}
|
||||
|
||||
static void clear_pending_entry_groups(struct module_zeroconf_publish_data *data)
|
||||
{
|
||||
struct service *s;
|
||||
|
||||
spa_list_for_each(s, &data->pending, link)
|
||||
clear_entry_group(s);
|
||||
}
|
||||
|
||||
static void client_callback(AvahiClient *c, AvahiClientState state, void *d)
|
||||
{
|
||||
struct module_zeroconf_publish_data *data = d;
|
||||
|
|
@ -478,27 +483,37 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *d)
|
|||
|
||||
switch (state) {
|
||||
case AVAHI_CLIENT_S_RUNNING:
|
||||
pw_log_info("the avahi daemon is up and running");
|
||||
publish_pending(data);
|
||||
break;
|
||||
case AVAHI_CLIENT_S_COLLISION:
|
||||
pw_log_error("Host name collision");
|
||||
unpublish_all_services(d, false);
|
||||
unpublish_all_services(d);
|
||||
break;
|
||||
case AVAHI_CLIENT_FAILURE:
|
||||
if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
|
||||
int error;
|
||||
{
|
||||
int err = avahi_client_errno(data->client);
|
||||
|
||||
pw_log_debug("Avahi daemon disconnected.");
|
||||
pw_log_error("avahi client failure: %s", avahi_strerror(err));
|
||||
|
||||
unpublish_all_services(d, true);
|
||||
avahi_client_free(data->client);
|
||||
unpublish_all_services(data);
|
||||
clear_pending_entry_groups(data);
|
||||
avahi_client_free(data->client);
|
||||
data->client = NULL;
|
||||
|
||||
if (!(data->client = avahi_client_new(data->avahi_poll,
|
||||
AVAHI_CLIENT_NO_FAIL, client_callback, data, &error))) {
|
||||
pw_log_error("avahi_client_new() failed: %s",
|
||||
avahi_strerror(error));
|
||||
module_schedule_unload(data->module);
|
||||
}
|
||||
if (err == AVAHI_ERR_DISCONNECTED) {
|
||||
data->client = avahi_client_new(data->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, data, &err);
|
||||
if (data->client == NULL)
|
||||
pw_log_error("avahi_client_new(): %s", avahi_strerror(err));
|
||||
}
|
||||
|
||||
if (data->client == NULL)
|
||||
module_schedule_unload(data->module);
|
||||
|
||||
break;
|
||||
}
|
||||
case AVAHI_CLIENT_CONNECTING:
|
||||
pw_log_info("connecting to the avahi daemon...");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -510,7 +525,11 @@ static void manager_removed(void *d, struct pw_manager_object *o)
|
|||
if (!pw_manager_object_is_sink(o) && !pw_manager_object_is_source(o))
|
||||
return;
|
||||
|
||||
service_free(d, o);
|
||||
struct service *s = pw_manager_object_get_data(o, SERVICE_DATA_ID);
|
||||
if (s == NULL)
|
||||
return;
|
||||
|
||||
service_free(s);
|
||||
}
|
||||
|
||||
static void manager_added(void *d, struct pw_manager_object *o)
|
||||
|
|
@ -573,8 +592,12 @@ static int module_zeroconf_publish_load(struct client *client, struct module *mo
|
|||
static int module_zeroconf_publish_unload(struct client *client, struct module *module)
|
||||
{
|
||||
struct module_zeroconf_publish_data *d = module->user_data;
|
||||
struct service *s;
|
||||
|
||||
pw_manager_for_each_object(d->manager, service_free, d);
|
||||
unpublish_all_services(d);
|
||||
|
||||
spa_list_consume(s, &d->pending, link)
|
||||
service_free(s);
|
||||
|
||||
if (d->client)
|
||||
avahi_client_free(d->client);
|
||||
|
|
@ -634,8 +657,9 @@ struct module *create_module_zeroconf_publish(struct impl *impl, const char *arg
|
|||
module->props = props;
|
||||
d = module->user_data;
|
||||
d->module = module;
|
||||
|
||||
d->port = pw_properties_get_uint32(props, "port", PW_PROTOCOL_PULSE_DEFAULT_PORT);
|
||||
spa_list_init(&d->pending);
|
||||
spa_list_init(&d->published);
|
||||
|
||||
return module;
|
||||
out:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue