Skip to content

Commit c194e9e

Browse files
committed
iiod: Move Avahi support to its own dns-sd.c source file
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]>
1 parent b86e5c9 commit c194e9e

File tree

5 files changed

+359
-324
lines changed

5 files changed

+359
-324
lines changed

iiod/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ endif ()
6060

6161
add_definitions(-D_GNU_SOURCE=1)
6262

63+
if (HAVE_AVAHI)
64+
target_sources(iiod PRIVATE dns-sd.c)
65+
endif()
66+
6367
if(NOT SKIP_INSTALL_ALL)
6468
install(TARGETS iiod RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
6569
endif()

iiod/dns-sd.c

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
// SPDX-license-identifier: LGPL-v2-or-later
2+
/*
3+
* libiio - Library for interfacing industrial I/O (IIO) devices
4+
*
5+
* Copyright (C) 2021 Analog Devices, Inc.
6+
* Author: Paul Cercueil <[email protected]>
7+
*/
8+
9+
#include "dns-sd.h"
10+
#include "ops.h"
11+
#include "thread-pool.h"
12+
13+
#include "../debug.h"
14+
#include "../iio.h"
15+
16+
#include <avahi-common/thread-watch.h>
17+
#include <avahi-common/error.h>
18+
#include <avahi-common/alternative.h>
19+
#include <avahi-common/malloc.h>
20+
#include <avahi-client/client.h>
21+
#include <avahi-client/publish.h>
22+
#include <avahi-common/domain.h>
23+
24+
#include <stddef.h>
25+
#include <time.h>
26+
27+
/***
28+
* Parts of the avahi code are borrowed from the client-publish-service.c
29+
* https://www.avahi.org/doxygen/html/client-publish-service_8c-example.html
30+
* which is an example in the avahi library. Copyright Lennart Poettering
31+
* released under the LGPL 2.1 or (at your option) any later version.
32+
*/
33+
34+
/* Global Data */
35+
36+
static struct avahi_data {
37+
AvahiThreadedPoll *poll;
38+
AvahiClient *client;
39+
AvahiEntryGroup *group;
40+
char * name;
41+
} avahi;
42+
43+
static void create_services(AvahiClient *c);
44+
static AvahiClient * client_new(void);
45+
46+
static void client_free()
47+
{
48+
/* This also frees the entry group, if any. */
49+
if (avahi.client) {
50+
avahi_client_free(avahi.client);
51+
avahi.client = NULL;
52+
avahi.group = NULL;
53+
}
54+
}
55+
56+
static void shutdown_avahi()
57+
{
58+
/* Stop the avahi client, if it's running. */
59+
if (avahi.poll)
60+
avahi_threaded_poll_stop(avahi.poll);
61+
62+
/* Clean up the avahi objects. The order is significant. */
63+
client_free();
64+
if (avahi.poll) {
65+
avahi_threaded_poll_free(avahi.poll);
66+
avahi.poll = NULL;
67+
}
68+
if (avahi.name) {
69+
IIO_INFO("Avahi: Removing service '%s'\n", avahi.name);
70+
avahi_free(avahi.name);
71+
avahi.name = NULL;
72+
}
73+
}
74+
75+
static void __avahi_group_cb(AvahiEntryGroup *group,
76+
AvahiEntryGroupState state, void *d)
77+
{
78+
/* Called whenever the entry group state changes */
79+
if (!group) {
80+
IIO_ERROR("__avahi_group_cb with no valid group\n");
81+
return;
82+
}
83+
84+
avahi.group = group;
85+
86+
switch (state) {
87+
case AVAHI_ENTRY_GROUP_ESTABLISHED :
88+
IIO_INFO("Avahi: Service '%s' successfully established.\n",
89+
avahi.name);
90+
break;
91+
case AVAHI_ENTRY_GROUP_COLLISION : {
92+
char *n;
93+
/* A service name collision, pick a new name */
94+
n = avahi_alternative_service_name(avahi.name);
95+
avahi_free(avahi.name);
96+
avahi.name = n;
97+
IIO_INFO("Avahi: Group Service name collision, renaming service to '%s'\n",
98+
avahi.name);
99+
create_services(avahi_entry_group_get_client(group));
100+
break;
101+
}
102+
case AVAHI_ENTRY_GROUP_FAILURE :
103+
IIO_ERROR("Entry group failure: %s\n",
104+
avahi_strerror(avahi_client_errno(
105+
avahi_entry_group_get_client(group))));
106+
break;
107+
case AVAHI_ENTRY_GROUP_UNCOMMITED:
108+
/* This is normal,
109+
* since we commit things in the create_services()
110+
*/
111+
IIO_DEBUG("Avahi: Group uncommitted\n");
112+
break;
113+
case AVAHI_ENTRY_GROUP_REGISTERING:
114+
IIO_DEBUG("Avahi: Group registering\n");
115+
break;
116+
}
117+
}
118+
119+
static void __avahi_client_cb(AvahiClient *client,
120+
AvahiClientState state, void *d)
121+
{
122+
if (!client) {
123+
IIO_ERROR("__avahi_client_cb with no valid client\n");
124+
return;
125+
}
126+
127+
switch (state) {
128+
case AVAHI_CLIENT_S_RUNNING:
129+
/* Same as AVAHI_SERVER_RUNNING */
130+
IIO_DEBUG("Avahi: create services\n");
131+
/* The server has startup successfully, so create our services */
132+
create_services(client);
133+
break;
134+
case AVAHI_CLIENT_FAILURE:
135+
if (avahi_client_errno(client) != AVAHI_ERR_DISCONNECTED) {
136+
IIO_ERROR("Avahi: Client failure: %s\n",
137+
avahi_strerror(avahi_client_errno(client)));
138+
break;
139+
}
140+
IIO_INFO("Avahi: server disconnected\n");
141+
avahi_client_free(client);
142+
avahi.group = NULL;
143+
avahi.client = client_new();
144+
break;
145+
case AVAHI_CLIENT_S_COLLISION:
146+
/* Same as AVAHI_SERVER_COLLISION */
147+
/* When the server is back in AVAHI_SERVER_RUNNING state
148+
* we will register them again with the new host name. */
149+
IIO_DEBUG("Avahi: Client collision\n");
150+
/* Let's drop our registered services.*/
151+
if (avahi.group)
152+
avahi_entry_group_reset(avahi.group);
153+
break;
154+
case AVAHI_CLIENT_S_REGISTERING:
155+
/* Same as AVAHI_SERVER_REGISTERING */
156+
IIO_DEBUG("Avahi: Client group reset\n");
157+
if (avahi.group)
158+
avahi_entry_group_reset(avahi.group);
159+
break;
160+
case AVAHI_CLIENT_CONNECTING:
161+
IIO_DEBUG("Avahi: Client Connecting\n");
162+
break;
163+
}
164+
165+
/* NOTE: group is freed by avahi_client_free */
166+
}
167+
168+
static AvahiClient * client_new(void)
169+
{
170+
int ret;
171+
AvahiClient * client;
172+
173+
client = avahi_client_new(avahi_threaded_poll_get(avahi.poll),
174+
AVAHI_CLIENT_NO_FAIL, __avahi_client_cb, NULL, &ret);
175+
176+
/* No Daemon is handled by the avahi_start thread */
177+
if (!client && ret != AVAHI_ERR_NO_DAEMON) {
178+
IIO_ERROR("Avahi: failure creating client: %s (%d)\n",
179+
avahi_strerror(ret), ret);
180+
}
181+
182+
return client;
183+
}
184+
185+
186+
static void create_services(AvahiClient *c)
187+
{
188+
int ret;
189+
190+
if (!c) {
191+
IIO_ERROR("create_services called with no valid client\n");
192+
goto fail;
193+
}
194+
195+
if (!avahi.group) {
196+
avahi.group = avahi_entry_group_new(c, __avahi_group_cb, NULL);
197+
if (!avahi.group) {
198+
IIO_ERROR("avahi_entry_group_new() failed: %s\n",
199+
avahi_strerror(avahi_client_errno(c)));
200+
goto fail;
201+
}
202+
}
203+
204+
if (!avahi_entry_group_is_empty(avahi.group)) {
205+
IIO_DEBUG("Avahi group not empty\n");
206+
return;
207+
}
208+
209+
ret = avahi_entry_group_add_service(avahi.group,
210+
AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
211+
0, avahi.name, "_iio._tcp", NULL, NULL, IIOD_PORT, NULL);
212+
if (ret < 0) {
213+
if (ret == AVAHI_ERR_COLLISION) {
214+
char *n;
215+
n = avahi_alternative_service_name(avahi.name);
216+
avahi_free(avahi.name);
217+
avahi.name = n;
218+
IIO_DEBUG("Service name collision, renaming service to '%s'\n",
219+
avahi.name);
220+
avahi_entry_group_reset(avahi.group);
221+
create_services(c);
222+
return;
223+
}
224+
IIO_ERROR("Failed to add _iio._tcp service: %s\n", avahi_strerror(ret));
225+
goto fail;
226+
}
227+
228+
ret = avahi_entry_group_commit(avahi.group);
229+
if (ret < 0) {
230+
IIO_ERROR("Failed to commit entry group: %s\n", avahi_strerror(ret));
231+
goto fail;
232+
}
233+
234+
IIO_INFO("Avahi: Registered '%s' to ZeroConf server %s\n",
235+
avahi.name, avahi_client_get_version_string(c));
236+
237+
return;
238+
239+
fail:
240+
avahi_entry_group_reset(avahi.group);
241+
}
242+
243+
#define IIOD_ON "iiod on "
244+
245+
static void start_avahi_thd(struct thread_pool *pool, void *d)
246+
{
247+
248+
struct pollfd pfd[2];
249+
int ret = ENOMEM;
250+
char label[AVAHI_LABEL_MAX];
251+
char host[AVAHI_LABEL_MAX - sizeof(IIOD_ON)];
252+
struct timespec ts;
253+
ts.tv_nsec = 0;
254+
ts.tv_sec = 1;
255+
256+
pfd[1].fd = thread_pool_get_poll_fd(pool);
257+
pfd[1].events = POLLIN;
258+
pfd[1].revents = 0;
259+
260+
while(true) {
261+
if (pfd[1].revents & POLLIN) /* STOP event */
262+
break;
263+
264+
ret = gethostname(host, sizeof(host));
265+
IIO_ERROR("host %s\n", host);
266+
if (ret || !strncmp(host, "none", sizeof("none") - 1))
267+
goto again;
268+
269+
iio_snprintf(label, sizeof(label), "%s%s", IIOD_ON, host);
270+
271+
if (!avahi.name)
272+
avahi.name = avahi_strdup(label);
273+
if (!avahi.name)
274+
break;
275+
276+
if (!avahi.poll)
277+
avahi.poll = avahi_threaded_poll_new();
278+
if (!avahi.poll) {
279+
goto again;
280+
}
281+
282+
if (!avahi.client)
283+
avahi.client = client_new();
284+
if (avahi.client)
285+
break;
286+
again:
287+
IIO_INFO("Avahi didn't start, try again later\n");
288+
nanosleep(&ts, NULL);
289+
ts.tv_sec++;
290+
/* If it hasn't started in 10 times over 60 seconds,
291+
* it is not going to, so stop
292+
*/
293+
if (ts.tv_sec >= 11)
294+
break;
295+
}
296+
297+
if (avahi.client && avahi.poll) {
298+
avahi_threaded_poll_start(avahi.poll);
299+
IIO_INFO("Avahi: Started.\n");
300+
} else {
301+
shutdown_avahi();
302+
IIO_INFO("Avahi: Failed to start.\n");
303+
}
304+
}
305+
306+
void start_avahi(struct thread_pool *pool)
307+
{
308+
int ret;
309+
char err_str[1024];
310+
311+
IIO_INFO("Attempting to start Avahi\n");
312+
313+
avahi.poll = NULL;
314+
avahi.client = NULL;
315+
avahi.group = NULL;
316+
avahi.name = NULL;
317+
318+
/* In case dbus, or avahi deamon are not started, we create a thread
319+
* that tries a few times to attach, if it can't within the first
320+
* minute, it gives up.
321+
*/
322+
ret = thread_pool_add_thread(pool, start_avahi_thd, NULL, "avahi_thd");
323+
if (ret) {
324+
iio_strerror(ret, err_str, sizeof(err_str));
325+
IIO_ERROR("Failed to create new Avahi thread: %s\n",
326+
err_str);
327+
}
328+
}
329+
330+
void stop_avahi(void)
331+
{
332+
shutdown_avahi();
333+
IIO_INFO("Avahi: Stopped\n");
334+
}

iiod/dns-sd.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* SPDX-license-identifier: LGPL-v2-or-later */
2+
/*
3+
* libiio - Library for interfacing industrial I/O (IIO) devices
4+
*
5+
* Copyright (C) 2021 Analog Devices, Inc.
6+
* Author: Paul Cercueil <[email protected]>
7+
*/
8+
9+
#ifndef __IIOD_DNS_SD_H
10+
#define __IIOD_DNS_SD_H
11+
12+
struct thread_pool;
13+
14+
void start_avahi(struct thread_pool *pool);
15+
void stop_avahi(void);
16+
17+
#endif /* __IIOD_DNS_SD_H */

0 commit comments

Comments
 (0)