2 #include "meshlink_internal.h"
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>
15 #define MESHLINK_MDNS_SERVICE_TYPE "_meshlink._tcp"
16 #define MESHLINK_MDNS_SERVICE_NAME "Meshlink"
19 static void discovery_entry_group_callback(AvahiServer *server, AvahiSEntryGroup *group, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata)
21 meshlink_handle_t *mesh = userdata;
23 /* Called whenever the entry group state changes */
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);
31 case AVAHI_ENTRY_GROUP_COLLISION:
32 fprintf(stderr, "Service name collision '%s'\n", MESHLINK_MDNS_SERVICE_NAME);
35 case AVAHI_ENTRY_GROUP_FAILURE :
36 fprintf(stderr, "Entry group failure: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
38 /* Some kind of failure happened while we were registering our services */
39 avahi_simple_poll_quit(mesh->avahi_poll);
42 case AVAHI_ENTRY_GROUP_UNCOMMITED:
43 case AVAHI_ENTRY_GROUP_REGISTERING:
49 static void discovery_create_services(meshlink_handle_t *mesh)
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)));
58 fprintf(stderr, "Adding service '%s'\n", MESHLINK_MDNS_SERVICE_NAME);
60 /* Create some random TXT data */
61 char fingerprint[1024];
62 snprintf(fingerprint, sizeof(fingerprint), "fingerprint=%s", /*meshlink_get_fingerprint(mesh, mesh->self)*/ "");
64 /* Add the service for IPP */
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));
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));
80 avahi_simple_poll_quit(mesh->avahi_poll);
83 static void discovery_server_callback(AvahiServer *server, AvahiServerState state, AVAHI_GCC_UNUSED void * userdata)
85 meshlink_handle_t *mesh = userdata;
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);
96 case AVAHI_SERVER_COLLISION:
97 /* A host name collision happened. Let's do nothing */
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);
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);
114 case AVAHI_SERVER_INVALID:
119 static void discovery_browse_callback(
120 AvahiSServiceBrowser *browser,
121 AvahiIfIndex interface,
122 AvahiProtocol protocol,
123 AvahiBrowserEvent event,
127 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
130 meshlink_handle_t *mesh = userdata;
132 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
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);
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)));
151 case AVAHI_BROWSER_REMOVE:
152 fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
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");
162 static void *discovery_loop(void *arg)
164 meshlink_handle_t *mesh = arg;
166 avahi_simple_poll_loop(mesh->avahi_poll);
171 bool discovery_start(meshlink_handle_t *mesh)
173 // Allocate discovery loop object
174 if (!(mesh->avahi_poll = avahi_simple_poll_new())) {
175 fprintf(stderr, "Failed to create discovery poll object.\n");
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;
185 /* Allocate a new server */
187 mesh->avahi_server = avahi_server_new(avahi_simple_poll_get(mesh->avahi_poll), &config, discovery_server_callback, NULL, &error);
189 /* Free the configuration data */
190 avahi_server_config_free(&config);
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));
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)));
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);
211 mesh->discovery_threadstarted = true;
216 if (mesh->avahi_browser)
217 avahi_s_service_browser_free(mesh->avahi_browser);
219 if (mesh->avahi_server)
220 avahi_server_free(mesh->avahi_server);
222 if (mesh->avahi_poll)
223 avahi_simple_poll_free(mesh->avahi_poll);
228 void discovery_stop(meshlink_handle_t *mesh)
231 avahi_simple_poll_quit(mesh->avahi_poll);
233 // Wait for the discovery thread to finish
235 pthread_join(mesh->discovery_thread, NULL);
237 // Clean up resources
238 if (mesh->avahi_browser)
239 avahi_s_service_browser_free(mesh->avahi_browser);
241 if (mesh->avahi_server)
242 avahi_server_free(mesh->avahi_server);
244 if (mesh->avahi_poll)
245 avahi_simple_poll_free(mesh->avahi_poll);