]> git.meshlink.io Git - meshlink/blob - src/discovery.c
avahi mdns skeleton
[meshlink] / src / discovery.c
1
2 #include "meshlink_internal.h"
3 #include "discovery.h"
4
5 #include <pthread.h>
6
7 #include <avahi-core/core.h>
8 #include <avahi-core/lookup.h>
9 #include <avahi-core/publish.h>
10 #include <avahi-common/simple-watch.h>
11 #include <avahi-common/malloc.h>
12 #include <avahi-common/alternative.h>
13 #include <avahi-common/error.h>
14
15 #define MESHLINK_MDNS_SERVICE_TYPE "_meshlink._tcp"
16 #define MESHLINK_MDNS_SERVICE_NAME "Meshlink"
17
18
19 static void discovery_entry_group_callback(AvahiServer *server, AvahiSEntryGroup *group, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata)
20 {
21     meshlink_handle_t *mesh = userdata;
22
23     /* Called whenever the entry group state changes */
24     switch (state)
25     {
26         case AVAHI_ENTRY_GROUP_ESTABLISHED:
27             /* The entry group has been established successfully */
28             fprintf(stderr, "Service '%s' successfully established.\n", MESHLINK_MDNS_SERVICE_NAME);
29             break;
30
31         case AVAHI_ENTRY_GROUP_COLLISION:
32             fprintf(stderr, "Service name collision '%s'\n", MESHLINK_MDNS_SERVICE_NAME);
33             break;
34
35         case AVAHI_ENTRY_GROUP_FAILURE :
36             fprintf(stderr, "Entry group failure: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
37
38             /* Some kind of failure happened while we were registering our services */
39             avahi_simple_poll_quit(mesh->avahi_poll);
40             break;
41
42         case AVAHI_ENTRY_GROUP_UNCOMMITED:
43         case AVAHI_ENTRY_GROUP_REGISTERING:
44             ;
45     }
46 }
47
48
49 static void discovery_create_services(meshlink_handle_t *mesh)
50 {
51     /* If this is the first time we're called, let's create a new entry group */
52     if (!mesh->avahi_group)
53         if (!(mesh->avahi_group = avahi_s_entry_group_new(mesh->avahi_server, discovery_entry_group_callback, NULL))) {
54             fprintf(stderr, "avahi_entry_group_new() failed: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
55             goto fail;
56         }
57
58     fprintf(stderr, "Adding service '%s'\n", MESHLINK_MDNS_SERVICE_NAME);
59
60     /* Create some random TXT data */
61     char fingerprint[1024];
62     snprintf(fingerprint, sizeof(fingerprint), "fingerprint=%s", /*meshlink_get_fingerprint(mesh, mesh->self)*/ "");
63
64     /* Add the service for IPP */
65     int ret = 0;
66     if ((ret = avahi_server_add_service(mesh->avahi_server, mesh->avahi_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, MESHLINK_MDNS_SERVICE_NAME, MESHLINK_MDNS_SERVICE_TYPE, NULL, NULL, mesh->myport ? atoi(mesh->myport) : 655, fingerprint, NULL)) < 0) {
67         fprintf(stderr, "Failed to add _ipp._tcp service: %s\n", avahi_strerror(ret));
68         goto fail;
69     }
70
71     /* Tell the server to register the service */
72     if ((ret = avahi_s_entry_group_commit(mesh->avahi_group)) < 0) {
73         fprintf(stderr, "Failed to commit entry_group: %s\n", avahi_strerror(ret));
74         goto fail;
75     }
76
77     return;
78
79 fail:
80     avahi_simple_poll_quit(mesh->avahi_poll);
81 }
82
83 static void discovery_server_callback(AvahiServer *server, AvahiServerState state, AVAHI_GCC_UNUSED void * userdata)
84 {
85         meshlink_handle_t *mesh = userdata;
86
87     switch (state)
88     {
89         case AVAHI_SERVER_RUNNING:
90             /* The serve has startup successfully and registered its host
91              * name on the network, so it's time to create our services */
92             if (!mesh->avahi_group)
93                 discovery_create_services(mesh);
94             break;
95
96         case AVAHI_SERVER_COLLISION:
97             /* A host name collision happened. Let's do nothing */
98             break;
99
100         case AVAHI_SERVER_REGISTERING:
101                 /* Let's drop our registered services. When the server is back
102              * in AVAHI_SERVER_RUNNING state we will register them
103              * again with the new host name. */
104             //if (mesh->avahi_group)
105             //    avahi_s_entry_group_reset(mesh->avahi_group);
106             break;
107
108         case AVAHI_SERVER_FAILURE:
109             /* Terminate on failure */
110             fprintf(stderr, "Server failure: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
111             avahi_simple_poll_quit(mesh->avahi_poll);
112             break;
113
114         case AVAHI_SERVER_INVALID:
115             break;
116     }
117 }
118
119 static void discovery_browse_callback(
120     AvahiSServiceBrowser *browser,
121     AvahiIfIndex interface,
122     AvahiProtocol protocol,
123     AvahiBrowserEvent event,
124     const char *name,
125     const char *type,
126     const char *domain,
127     AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
128     void* userdata)
129 {
130         meshlink_handle_t *mesh = userdata;
131
132     /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
133
134     switch (event)
135     {
136         case AVAHI_BROWSER_FAILURE:
137             fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
138             avahi_simple_poll_quit(mesh->avahi_poll);
139             return;
140
141         case AVAHI_BROWSER_NEW:
142             fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
143             /* We ignore the returned resolver object. In the callback
144                function we free it. If the server is terminated before
145                the callback function is called the server will free
146                the resolver for us. */
147             //if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, server)))
148             //    fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(server)));
149             break;
150
151         case AVAHI_BROWSER_REMOVE:
152             fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
153             break;
154
155         case AVAHI_BROWSER_ALL_FOR_NOW:
156         case AVAHI_BROWSER_CACHE_EXHAUSTED:
157             fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
158             break;
159     }
160 }
161
162 static void *discovery_loop(void *arg)
163 {
164         meshlink_handle_t *mesh = arg;
165
166     avahi_simple_poll_loop(mesh->avahi_poll);
167
168         return NULL;
169 }
170
171 bool discovery_start(meshlink_handle_t *mesh)
172 {
173     // Allocate discovery loop object
174     if (!(mesh->avahi_poll = avahi_simple_poll_new())) {
175         fprintf(stderr, "Failed to create discovery poll object.\n");
176                 goto fail;
177     }
178
179     // Let's set the host name for this server.
180     AvahiServerConfig config;
181     avahi_server_config_init(&config);
182     config.host_name = avahi_strdup(mesh->name);
183     config.publish_workstation = 0;
184
185     /* Allocate a new server */
186     int error;
187     mesh->avahi_server = avahi_server_new(avahi_simple_poll_get(mesh->avahi_poll), &config, discovery_server_callback, NULL, &error);
188
189     /* Free the configuration data */
190     avahi_server_config_free(&config);
191
192     /* Check wether creating the server object succeeded */
193     if (!mesh->avahi_server) {
194         fprintf(stderr, "Failed to create discovery server: %s\n", avahi_strerror(error));
195         goto fail;
196     }
197
198     // Create the service browser
199     if (!(mesh->avahi_browser = avahi_s_service_browser_new(mesh->avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, MESHLINK_MDNS_SERVICE_TYPE, NULL, 0, discovery_browse_callback, mesh))) {
200         fprintf(stderr, "Failed to create discovery service browser: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
201         goto fail;
202     }
203
204         // Start the discovery thread
205         if(pthread_create(&mesh->discovery_thread, NULL, discovery_loop, mesh) != 0) {
206                 fprintf(stderr, "Could not start discovery thread: %s\n", strerror(errno));
207                 memset(&mesh->discovery_thread, 0, sizeof mesh->discovery_thread);
208                 goto fail;
209         }
210
211         mesh->discovery_threadstarted = true;
212
213         return true;
214
215 fail:
216     if (mesh->avahi_browser)
217         avahi_s_service_browser_free(mesh->avahi_browser);
218
219     if (mesh->avahi_server)
220         avahi_server_free(mesh->avahi_server);
221
222     if (mesh->avahi_poll)
223         avahi_simple_poll_free(mesh->avahi_poll);
224
225     return false;
226 }
227
228 void discovery_stop(meshlink_handle_t *mesh)
229 {
230         // @TODO: Shut down 
231         avahi_simple_poll_quit(mesh->avahi_poll);
232
233         // Wait for the discovery thread to finish
234
235         pthread_join(mesh->discovery_thread, NULL);
236
237         // Clean up resources
238     if (mesh->avahi_browser)
239         avahi_s_service_browser_free(mesh->avahi_browser);
240
241     if (mesh->avahi_server)
242         avahi_server_free(mesh->avahi_server);
243
244     if (mesh->avahi_poll)
245         avahi_simple_poll_free(mesh->avahi_poll);
246 }