Skip to content

Commit

Permalink
iiod: Move Avahi support to its own dns-sd.c source file
Browse files Browse the repository at this point in the history
Avoid having a huge #ifdef block in iiod.c by moving this code to a
dns-sd.c source file, that will be compiled conditionally.

Signed-off-by: Paul Cercueil <[email protected]>
  • Loading branch information
pcercuei committed Apr 9, 2021
1 parent b86e5c9 commit c194e9e
Show file tree
Hide file tree
Showing 5 changed files with 359 additions and 324 deletions.
4 changes: 4 additions & 0 deletions iiod/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ endif ()

add_definitions(-D_GNU_SOURCE=1)

if (HAVE_AVAHI)
target_sources(iiod PRIVATE dns-sd.c)
endif()

if(NOT SKIP_INSTALL_ALL)
install(TARGETS iiod RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
endif()
Expand Down
334 changes: 334 additions & 0 deletions iiod/dns-sd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
// SPDX-license-identifier: LGPL-v2-or-later
/*
* libiio - Library for interfacing industrial I/O (IIO) devices
*
* Copyright (C) 2021 Analog Devices, Inc.
* Author: Paul Cercueil <[email protected]>
*/

#include "dns-sd.h"
#include "ops.h"
#include "thread-pool.h"

#include "../debug.h"
#include "../iio.h"

#include <avahi-common/thread-watch.h>
#include <avahi-common/error.h>
#include <avahi-common/alternative.h>
#include <avahi-common/malloc.h>
#include <avahi-client/client.h>
#include <avahi-client/publish.h>
#include <avahi-common/domain.h>

#include <stddef.h>
#include <time.h>

/***
* Parts of the avahi code are borrowed from the client-publish-service.c
* https://www.avahi.org/doxygen/html/client-publish-service_8c-example.html
* which is an example in the avahi library. Copyright Lennart Poettering
* released under the LGPL 2.1 or (at your option) any later version.
*/

/* Global Data */

static struct avahi_data {
AvahiThreadedPoll *poll;
AvahiClient *client;
AvahiEntryGroup *group;
char * name;
} avahi;

static void create_services(AvahiClient *c);
static AvahiClient * client_new(void);

static void client_free()
{
/* This also frees the entry group, if any. */
if (avahi.client) {
avahi_client_free(avahi.client);
avahi.client = NULL;
avahi.group = NULL;
}
}

static void shutdown_avahi()
{
/* Stop the avahi client, if it's running. */
if (avahi.poll)
avahi_threaded_poll_stop(avahi.poll);

/* Clean up the avahi objects. The order is significant. */
client_free();
if (avahi.poll) {
avahi_threaded_poll_free(avahi.poll);
avahi.poll = NULL;
}
if (avahi.name) {
IIO_INFO("Avahi: Removing service '%s'\n", avahi.name);
avahi_free(avahi.name);
avahi.name = NULL;
}
}

static void __avahi_group_cb(AvahiEntryGroup *group,
AvahiEntryGroupState state, void *d)
{
/* Called whenever the entry group state changes */
if (!group) {
IIO_ERROR("__avahi_group_cb with no valid group\n");
return;
}

avahi.group = group;

switch (state) {
case AVAHI_ENTRY_GROUP_ESTABLISHED :
IIO_INFO("Avahi: Service '%s' successfully established.\n",
avahi.name);
break;
case AVAHI_ENTRY_GROUP_COLLISION : {
char *n;
/* A service name collision, pick a new name */
n = avahi_alternative_service_name(avahi.name);
avahi_free(avahi.name);
avahi.name = n;
IIO_INFO("Avahi: Group Service name collision, renaming service to '%s'\n",
avahi.name);
create_services(avahi_entry_group_get_client(group));
break;
}
case AVAHI_ENTRY_GROUP_FAILURE :
IIO_ERROR("Entry group failure: %s\n",
avahi_strerror(avahi_client_errno(
avahi_entry_group_get_client(group))));
break;
case AVAHI_ENTRY_GROUP_UNCOMMITED:
/* This is normal,
* since we commit things in the create_services()
*/
IIO_DEBUG("Avahi: Group uncommitted\n");
break;
case AVAHI_ENTRY_GROUP_REGISTERING:
IIO_DEBUG("Avahi: Group registering\n");
break;
}
}

static void __avahi_client_cb(AvahiClient *client,
AvahiClientState state, void *d)
{
if (!client) {
IIO_ERROR("__avahi_client_cb with no valid client\n");
return;
}

switch (state) {
case AVAHI_CLIENT_S_RUNNING:
/* Same as AVAHI_SERVER_RUNNING */
IIO_DEBUG("Avahi: create services\n");
/* The server has startup successfully, so create our services */
create_services(client);
break;
case AVAHI_CLIENT_FAILURE:
if (avahi_client_errno(client) != AVAHI_ERR_DISCONNECTED) {
IIO_ERROR("Avahi: Client failure: %s\n",
avahi_strerror(avahi_client_errno(client)));
break;
}
IIO_INFO("Avahi: server disconnected\n");
avahi_client_free(client);
avahi.group = NULL;
avahi.client = client_new();
break;
case AVAHI_CLIENT_S_COLLISION:
/* Same as AVAHI_SERVER_COLLISION */
/* When the server is back in AVAHI_SERVER_RUNNING state
* we will register them again with the new host name. */
IIO_DEBUG("Avahi: Client collision\n");
/* Let's drop our registered services.*/
if (avahi.group)
avahi_entry_group_reset(avahi.group);
break;
case AVAHI_CLIENT_S_REGISTERING:
/* Same as AVAHI_SERVER_REGISTERING */
IIO_DEBUG("Avahi: Client group reset\n");
if (avahi.group)
avahi_entry_group_reset(avahi.group);
break;
case AVAHI_CLIENT_CONNECTING:
IIO_DEBUG("Avahi: Client Connecting\n");
break;
}

/* NOTE: group is freed by avahi_client_free */
}

static AvahiClient * client_new(void)
{
int ret;
AvahiClient * client;

client = avahi_client_new(avahi_threaded_poll_get(avahi.poll),
AVAHI_CLIENT_NO_FAIL, __avahi_client_cb, NULL, &ret);

/* No Daemon is handled by the avahi_start thread */
if (!client && ret != AVAHI_ERR_NO_DAEMON) {
IIO_ERROR("Avahi: failure creating client: %s (%d)\n",
avahi_strerror(ret), ret);
}

return client;
}


static void create_services(AvahiClient *c)
{
int ret;

if (!c) {
IIO_ERROR("create_services called with no valid client\n");
goto fail;
}

if (!avahi.group) {
avahi.group = avahi_entry_group_new(c, __avahi_group_cb, NULL);
if (!avahi.group) {
IIO_ERROR("avahi_entry_group_new() failed: %s\n",
avahi_strerror(avahi_client_errno(c)));
goto fail;
}
}

if (!avahi_entry_group_is_empty(avahi.group)) {
IIO_DEBUG("Avahi group not empty\n");
return;
}

ret = avahi_entry_group_add_service(avahi.group,
AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
0, avahi.name, "_iio._tcp", NULL, NULL, IIOD_PORT, NULL);
if (ret < 0) {
if (ret == AVAHI_ERR_COLLISION) {
char *n;
n = avahi_alternative_service_name(avahi.name);
avahi_free(avahi.name);
avahi.name = n;
IIO_DEBUG("Service name collision, renaming service to '%s'\n",
avahi.name);
avahi_entry_group_reset(avahi.group);
create_services(c);
return;
}
IIO_ERROR("Failed to add _iio._tcp service: %s\n", avahi_strerror(ret));
goto fail;
}

ret = avahi_entry_group_commit(avahi.group);
if (ret < 0) {
IIO_ERROR("Failed to commit entry group: %s\n", avahi_strerror(ret));
goto fail;
}

IIO_INFO("Avahi: Registered '%s' to ZeroConf server %s\n",
avahi.name, avahi_client_get_version_string(c));

return;

fail:
avahi_entry_group_reset(avahi.group);
}

#define IIOD_ON "iiod on "

static void start_avahi_thd(struct thread_pool *pool, void *d)
{

struct pollfd pfd[2];
int ret = ENOMEM;
char label[AVAHI_LABEL_MAX];
char host[AVAHI_LABEL_MAX - sizeof(IIOD_ON)];
struct timespec ts;
ts.tv_nsec = 0;
ts.tv_sec = 1;

pfd[1].fd = thread_pool_get_poll_fd(pool);
pfd[1].events = POLLIN;
pfd[1].revents = 0;

while(true) {
if (pfd[1].revents & POLLIN) /* STOP event */
break;

ret = gethostname(host, sizeof(host));
IIO_ERROR("host %s\n", host);
if (ret || !strncmp(host, "none", sizeof("none") - 1))
goto again;

iio_snprintf(label, sizeof(label), "%s%s", IIOD_ON, host);

if (!avahi.name)
avahi.name = avahi_strdup(label);
if (!avahi.name)
break;

if (!avahi.poll)
avahi.poll = avahi_threaded_poll_new();
if (!avahi.poll) {
goto again;
}

if (!avahi.client)
avahi.client = client_new();
if (avahi.client)
break;
again:
IIO_INFO("Avahi didn't start, try again later\n");
nanosleep(&ts, NULL);
ts.tv_sec++;
/* If it hasn't started in 10 times over 60 seconds,
* it is not going to, so stop
*/
if (ts.tv_sec >= 11)
break;
}

if (avahi.client && avahi.poll) {
avahi_threaded_poll_start(avahi.poll);
IIO_INFO("Avahi: Started.\n");
} else {
shutdown_avahi();
IIO_INFO("Avahi: Failed to start.\n");
}
}

void start_avahi(struct thread_pool *pool)
{
int ret;
char err_str[1024];

IIO_INFO("Attempting to start Avahi\n");

avahi.poll = NULL;
avahi.client = NULL;
avahi.group = NULL;
avahi.name = NULL;

/* In case dbus, or avahi deamon are not started, we create a thread
* that tries a few times to attach, if it can't within the first
* minute, it gives up.
*/
ret = thread_pool_add_thread(pool, start_avahi_thd, NULL, "avahi_thd");
if (ret) {
iio_strerror(ret, err_str, sizeof(err_str));
IIO_ERROR("Failed to create new Avahi thread: %s\n",
err_str);
}
}

void stop_avahi(void)
{
shutdown_avahi();
IIO_INFO("Avahi: Stopped\n");
}
17 changes: 17 additions & 0 deletions iiod/dns-sd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* SPDX-license-identifier: LGPL-v2-or-later */
/*
* libiio - Library for interfacing industrial I/O (IIO) devices
*
* Copyright (C) 2021 Analog Devices, Inc.
* Author: Paul Cercueil <[email protected]>
*/

#ifndef __IIOD_DNS_SD_H
#define __IIOD_DNS_SD_H

struct thread_pool;

void start_avahi(struct thread_pool *pool);
void stop_avahi(void);

#endif /* __IIOD_DNS_SD_H */
Loading

0 comments on commit c194e9e

Please sign in to comment.