1/* 2 * Rendezvous support with avahi 3 * 4 * Copyright (C) 2005 Sebastian Dr��ge <slomo@ubuntu.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21#ifdef HAVE_CONFIG_H 22#include "config.h" 23#endif 24 25#include <stdio.h> 26#include <stdlib.h> 27#include <string.h> 28#include <errno.h> 29#include <assert.h> 30#include <pthread.h> 31#include <net/if.h> 32 33 34#include <avahi-client/client.h> 35#include <avahi-client/publish.h> 36 37#include <avahi-client/client.h> 38#include <avahi-common/alternative.h> 39#include <avahi-common/error.h> 40#include <avahi-common/simple-watch.h> 41#include <avahi-common/timeval.h> 42#include <avahi-common/malloc.h> 43 44#include "daapd.h" 45#include "err.h" 46 47static AvahiClient *mdns_client = NULL; 48static AvahiEntryGroup *mdns_group = NULL; 49static AvahiSimplePoll *simple_poll = NULL; 50 51typedef struct tag_rend_avahi_group_entry { 52 char *name; 53 char *type; 54 int port; 55 struct tag_rend_avahi_group_entry *next; 56} REND_AVAHI_GROUP_ENTRY; 57 58static REND_AVAHI_GROUP_ENTRY rend_avahi_entries = { NULL, NULL, 0, NULL }; 59 60static pthread_t rend_tid; 61static pthread_cond_t rend_avahi_cond; 62static pthread_mutex_t rend_avahi_mutex; 63 64static void _rend_avahi_signal(void); 65static void _rend_avahi_wait_on(void *what); 66static void _rend_avahi_lock(void); 67static void _rend_avahi_unlock(void); 68static int _rend_avahi_create_services(void); 69 70/* add a new group entry node */ 71static int _rend_avahi_add_group_entry(char *name, char *type, int port) { 72 REND_AVAHI_GROUP_ENTRY *pge; 73 74 pge = (REND_AVAHI_GROUP_ENTRY *)malloc(sizeof(REND_AVAHI_GROUP_ENTRY)); 75 if(!pge) 76 return 0; 77 78 pge->name = strdup(name); 79 pge->type = strdup(type); 80 pge->port = port; 81 82 _rend_avahi_lock(); 83 84 pge->next = rend_avahi_entries.next; 85 rend_avahi_entries.next = pge; 86 87 _rend_avahi_unlock(); 88 return 1; 89} 90 91static void *rend_poll(void *arg) { 92 int ret; 93 while((ret = avahi_simple_poll_iterate(simple_poll,-1)) == 0); 94 95 if(ret < 0) { 96 DPRINTF(E_WARN,L_REND,"Avahi poll thread quit iwth error: %s\n", 97 avahi_strerror(avahi_client_errno(mdns_client))); 98 } else { 99 DPRINTF(E_DBG,L_REND,"Avahi poll thread quit\n"); 100 } 101 102 return NULL; 103} 104 105static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { 106 // assert(g == mdns_group); 107 108 switch (state) { 109 case AVAHI_ENTRY_GROUP_ESTABLISHED: 110 DPRINTF(E_DBG, L_REND, "Successfully added mdns services\n"); 111 _rend_avahi_signal(); 112 break; 113 case AVAHI_ENTRY_GROUP_COLLISION: 114 DPRINTF(E_DBG, L_REND, "Group collision\n"); 115 /* 116 new_name = avahi_alternative_service_name(mdns_name); 117 DPRINTF(E_WARN, L_REND, "mdns service name collision. Renamed service %s -> %s\n", mdns_name, new_name); 118 free(mdns_name); 119 mdns_name = new_name; 120 add_services(avahi_entry_group_get_client(g)); 121 */ 122 break; 123 case AVAHI_ENTRY_GROUP_FAILURE : 124 avahi_simple_poll_quit(simple_poll); 125 break; 126 case AVAHI_ENTRY_GROUP_UNCOMMITED: 127 case AVAHI_ENTRY_GROUP_REGISTERING: 128 break; 129 } 130} 131 132void _rend_avahi_lock(void) { 133 if(pthread_mutex_lock(&rend_avahi_mutex)) 134 DPRINTF(E_FATAL,L_REND,"Could not lock mutex\n"); 135} 136 137void _rend_avahi_unlock(void) { 138 pthread_mutex_unlock(&rend_avahi_mutex); 139} 140 141void _rend_avahi_signal(void) { 142 /* kick the condition for waiters */ 143 _rend_avahi_lock(); 144 pthread_cond_signal(&rend_avahi_cond); 145 _rend_avahi_unlock(); 146} 147 148void _rend_avahi_wait_on(void *what) { 149 DPRINTF(E_DBG,L_REND,"Waiting on something...\n"); 150 if(pthread_mutex_lock(&rend_avahi_mutex)) 151 DPRINTF(E_FATAL,L_REND,"Could not lock mutex\n"); 152 while(!what) { 153 pthread_cond_wait(&rend_avahi_cond,&rend_avahi_mutex); 154 } 155 _rend_avahi_unlock(); 156 DPRINTF(E_DBG,L_REND,"Done waiting.\n"); 157} 158 159int rend_register(char *name, char *type, int port) { 160 DPRINTF(E_DBG,L_REND,"Adding %s/%s\n",name,type); 161 _rend_avahi_add_group_entry(name,type,port); 162 if(mdns_group) { 163 DPRINTF(E_DBG,L_MISC,"Resetting mdns group\n"); 164 avahi_entry_group_reset(mdns_group); 165 } 166 DPRINTF(E_DBG,L_REND,"Creating service group (again?)\n"); 167 _rend_avahi_create_services(); 168 return 0; 169} 170 171 172/** 173 * register the block of services 174 * 175 * @returns true if successful, false otherwise 176 */ 177int _rend_avahi_create_services(void) { 178 int ret = 0; 179 REND_AVAHI_GROUP_ENTRY *pentry; 180 AvahiStringList *psl; 181 unsigned char count=0; 182 183 DPRINTF(E_DBG,L_REND,"Creting service group\n"); 184 185 if(!rend_avahi_entries.next) { 186 DPRINTF(E_DBG,L_REND,"No entries yet... skipping service create\n"); 187 return 1; 188 } 189 190 if (mdns_group == NULL) { 191 if (!(mdns_group = avahi_entry_group_new(mdns_client, 192 entry_group_callback, 193 NULL))) { 194 DPRINTF(E_WARN, L_REND, "Could not create AvahiEntryGroup: %s\n", 195 avahi_strerror(avahi_client_errno(mdns_client))); 196 return 0; 197 } 198 } 199 200 /* wait for entry group to be created */ 201 _rend_avahi_wait_on(mdns_group); 202 203 _rend_avahi_lock(); 204 pentry = rend_avahi_entries.next; 205 while(pentry) { 206 /* TODO: honor iface parameter */ 207 DPRINTF(E_DBG,L_REND,"Re-registering %s/%s\n",pentry->name,pentry->type); 208 209 /* build a string list */ 210 psl = NULL; 211 psl=avahi_string_list_add(psl,"txtvers=1"); 212 psl=avahi_string_list_add(psl,"Database ID=beddab1edeadbea7"); 213 214 if ((ret = avahi_entry_group_add_service_strlst(mdns_group, 215 AVAHI_IF_UNSPEC, 216 AVAHI_PROTO_UNSPEC, 0, 217 avahi_strdup(pentry->name), 218 avahi_strdup(pentry->type), 219 NULL, NULL,pentry->port, 220 psl)) < 0) { 221 DPRINTF(E_WARN, L_REND, "Could not add mdns services: %s\n", avahi_strerror(ret)); 222 avahi_string_list_free(psl); 223 _rend_avahi_unlock(); 224 return 0; 225 } 226 pentry = pentry->next; 227 } 228 229 _rend_avahi_unlock(); 230 231 if ((ret = avahi_entry_group_commit(mdns_group)) < 0) { 232 DPRINTF(E_WARN, L_REND, "Could not commit mdns services: %s\n", 233 avahi_strerror(avahi_client_errno(mdns_client))); 234 return 0; 235 } 236 237 return 1; 238} 239 240static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { 241 assert(c); 242 switch(state) { 243 case AVAHI_CLIENT_S_RUNNING: 244 DPRINTF(E_LOG,L_REND,"Client running\n"); 245 if(!mdns_group) 246 _rend_avahi_create_services(); 247 break; 248 case AVAHI_CLIENT_S_COLLISION: 249 DPRINTF(E_LOG,L_REND,"Client collision\n"); 250 if(mdns_group) 251 avahi_entry_group_reset(mdns_group); 252 break; 253 case AVAHI_CLIENT_FAILURE: 254 DPRINTF(E_LOG,L_REND,"Client failure\n"); 255 avahi_simple_poll_quit(simple_poll); 256 break; 257 case AVAHI_CLIENT_S_REGISTERING: 258 DPRINTF(E_LOG,L_REND,"Client registering\n"); 259 if(mdns_group) 260 avahi_entry_group_reset(mdns_group); 261 break; 262 case AVAHI_CLIENT_CONNECTING: 263 break; 264 } 265} 266 267int rend_init(char *user) { 268 int error; 269 270 if(pthread_cond_init(&rend_avahi_cond,NULL)) { 271 DPRINTF(E_LOG,L_REND,"Could not initialize rendezvous condition\n"); 272 return -1; 273 } 274 275 if(pthread_mutex_init(&rend_avahi_mutex,NULL)) { 276 DPRINTF(E_LOG,L_REND,"Could not initialize rendezvous mutex\n"); 277 return -1; 278 } 279 280 DPRINTF(E_DBG, L_REND, "Initializing avahi\n"); 281 if(!(simple_poll = avahi_simple_poll_new())) { 282 DPRINTF(E_LOG,L_REND,"Error starting poll thread\n"); 283 return -1; 284 } 285 286 /* 287 mdns_name = strdup(name); 288 mdns_port = port; 289 // if ((interface != NULL) && (if_nametoindex(interface) != 0)) 290 // mdns_interface = if_nametoindex(interface); 291 // else 292 mdns_interface = AVAHI_IF_UNSPEC; 293 */ 294 295 if (!(mdns_client = avahi_client_new(avahi_simple_poll_get(simple_poll), 296 0,client_callback,NULL,&error))) { 297 DPRINTF(E_WARN, L_REND, "avahi_client_new: Error in avahi: %s\n", 298 avahi_strerror(avahi_client_errno(mdns_client))); 299 avahi_simple_poll_free(simple_poll); 300 return -1; 301 } 302 303 DPRINTF(E_DBG, L_REND, "Starting avahi polling thread\n"); 304 if(pthread_create(&rend_tid, NULL, rend_poll, NULL)) { 305 DPRINTF(E_FATAL,L_REND,"Could not start avahi polling thread.\n"); 306 } 307 308 return 0; 309} 310 311int rend_stop() { 312 avahi_simple_poll_quit(simple_poll); 313 if (mdns_client != NULL) 314 avahi_client_free(mdns_client); 315 return 0; 316} 317 318int rend_running(void) { 319 return 1; 320} 321 322int rend_unregister(char *name, char *type, int port) { 323 return 0; 324} 325 326