announce.c revision 8b5cd6ffd9137b14b7ed678f10a551e3911e4a40
1/* $Id$ */ 2 3/*** 4 This file is part of avahi. 5 6 avahi is free software; you can redistribute it and/or modify it 7 under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 2.1 of the 9 License, or (at your option) any later version. 10 11 avahi is distributed in the hope that it will be useful, but WITHOUT 12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General 14 Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with avahi; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 19 USA. 20***/ 21 22#ifdef HAVE_CONFIG_H 23#include <config.h> 24#endif 25 26#include <avahi-common/timeval.h> 27#include <avahi-common/malloc.h> 28 29#include "announce.h" 30#include "log.h" 31 32#define AVAHI_ANNOUNCEMENT_JITTER_MSEC 250 33#define AVAHI_PROBE_JITTER_MSEC 250 34#define AVAHI_PROBE_INTERVAL_MSEC 250 35 36static void remove_announcement(AvahiServer *s, AvahiAnnouncement *a) { 37 assert(s); 38 assert(a); 39 40 if (a->time_event) 41 avahi_time_event_free(a->time_event); 42 43 AVAHI_LLIST_REMOVE(AvahiAnnouncement, by_interface, a->interface->announcements, a); 44 AVAHI_LLIST_REMOVE(AvahiAnnouncement, by_entry, a->entry->announcements, a); 45 46 avahi_free(a); 47} 48 49static void elapse_announce(AvahiTimeEvent *e, void *userdata); 50 51static void set_timeout(AvahiAnnouncement *a, const struct timeval *tv) { 52 assert(a); 53 54 if (!tv) { 55 if (a->time_event) { 56 avahi_time_event_free(a->time_event); 57 a->time_event = NULL; 58 } 59 } else { 60 61 if (a->time_event) 62 avahi_time_event_update(a->time_event, tv); 63 else 64 a->time_event = avahi_time_event_new(a->server->time_event_queue, tv, elapse_announce, a); 65 } 66} 67 68static void next_state(AvahiAnnouncement *a); 69 70void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately) { 71 AvahiEntry *e; 72 assert(g); 73 assert(!g->dead); 74 75 /* Check whether all group members have been probed */ 76 77 if (g->state != AVAHI_ENTRY_GROUP_REGISTERING || g->n_probing > 0) 78 return; 79 80 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_ESTABLISHED); 81 82 if (g->dead) 83 return; 84 85 for (e = g->entries; e; e = e->entries_next) { 86 AvahiAnnouncement *a; 87 88 for (a = e->announcements; a; a = a->by_entry_next) { 89 90 if (a->state != AVAHI_WAITING) 91 continue; 92 93 a->state = AVAHI_ANNOUNCING; 94 95 if (immediately) { 96 /* Shortcut */ 97 98 a->n_iteration = 1; 99 next_state(a); 100 } else { 101 struct timeval tv; 102 a->n_iteration = 0; 103 avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC); 104 set_timeout(a, &tv); 105 } 106 } 107 } 108} 109 110static void next_state(AvahiAnnouncement *a) { 111 assert(a); 112 113/* avahi_log_debug("%i -- %u", a->state, a->n_iteration); */ 114 115 if (a->state == AVAHI_WAITING) { 116 117 assert(a->entry->group); 118 119 avahi_s_entry_group_check_probed(a->entry->group, 1); 120 121 } else if (a->state == AVAHI_PROBING) { 122 123 if (a->n_iteration >= 4) { 124 /* Probing done */ 125 126/* char *t; */ 127 128/* avahi_log_debug("Enough probes for record [%s]", t = avahi_record_to_string(a->entry->record)); */ 129/* avahi_free(t); */ 130 131 if (a->entry->group) { 132 assert(a->entry->group->n_probing); 133 a->entry->group->n_probing--; 134 } 135 136 if (a->entry->group && a->entry->group->state == AVAHI_ENTRY_GROUP_REGISTERING) 137 a->state = AVAHI_WAITING; 138 else { 139 a->state = AVAHI_ANNOUNCING; 140 a->n_iteration = 1; 141 } 142 143 set_timeout(a, NULL); 144 next_state(a); 145 } else { 146 struct timeval tv; 147 148 avahi_interface_post_probe(a->interface, a->entry->record, 0); 149 150 avahi_elapse_time(&tv, AVAHI_PROBE_INTERVAL_MSEC, 0); 151 set_timeout(a, &tv); 152 153 a->n_iteration++; 154 } 155 156 } else if (a->state == AVAHI_ANNOUNCING) { 157 158 if (a->entry->flags & AVAHI_ENTRY_UNIQUE) 159 /* Send the whole rrset at once */ 160 avahi_server_prepare_matching_responses(a->server, a->interface, a->entry->record->key, 0); 161 else 162 avahi_server_prepare_response(a->server, a->interface, a->entry, 0, 0); 163 164 avahi_server_generate_response(a->server, a->interface, NULL, NULL, 0, 0, 0); 165 166 if (++a->n_iteration >= 4) { 167/* char *t; */ 168 /* Announcing done */ 169 170/* avahi_log_debug("Enough announcements for record [%s]", t = avahi_record_to_string(a->entry->record)); */ 171/* avahi_free(t); */ 172 173 a->state = AVAHI_ESTABLISHED; 174 175 set_timeout(a, NULL); 176 } else { 177 struct timeval tv; 178 avahi_elapse_time(&tv, a->sec_delay*1000, AVAHI_ANNOUNCEMENT_JITTER_MSEC); 179 180 if (a->n_iteration < 10) 181 a->sec_delay *= 2; 182 183 set_timeout(a, &tv); 184 } 185 } 186} 187 188static void elapse_announce(AvahiTimeEvent *e, void *userdata) { 189 assert(e); 190 191 next_state(userdata); 192} 193 194AvahiAnnouncement *avahi_get_announcement(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { 195 AvahiAnnouncement *a; 196 197 assert(s); 198 assert(e); 199 assert(i); 200 201 for (a = e->announcements; a; a = a->by_entry_next) 202 if (a->interface == i) 203 return a; 204 205 return NULL; 206} 207 208static void go_to_initial_state(AvahiAnnouncement *a, int immediately) { 209 AvahiEntry *e; 210 struct timeval tv; 211 212 assert(a); 213 e = a->entry; 214 215 if ((e->flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_NOPROBE)) 216 a->state = AVAHI_PROBING; 217 else if (!(e->flags & AVAHI_ENTRY_NOANNOUNCE)) { 218 219 if (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED) 220 a->state = AVAHI_ANNOUNCING; 221 else 222 a->state = AVAHI_WAITING; 223 224 } else 225 a->state = AVAHI_ESTABLISHED; 226 227 a->n_iteration = 1; 228 a->sec_delay = 1; 229 230 if (a->state == AVAHI_PROBING && e->group) 231 e->group->n_probing++; 232 233 if (a->state == AVAHI_PROBING) { 234 avahi_elapse_time(&tv, 0, immediately ? 0 : AVAHI_PROBE_JITTER_MSEC); 235 set_timeout(a, &tv); 236 } else if (a->state == AVAHI_ANNOUNCING) { 237 avahi_elapse_time(&tv, 0, immediately ? 0 : AVAHI_ANNOUNCEMENT_JITTER_MSEC); 238 set_timeout(a, &tv); 239 } else 240 set_timeout(a, NULL); 241} 242 243static void new_announcement(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) { 244 AvahiAnnouncement *a; 245/* char *t; */ 246 247 assert(s); 248 assert(i); 249 assert(e); 250 assert(!e->dead); 251 252/* avahi_log_debug("NEW ANNOUNCEMENT: %s.%i [%s]", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record)); */ 253/* avahi_free(t); */ 254 255 if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_is_commited(e)) 256 return; 257 258 /* We don't want duplicate announcements */ 259 if (avahi_get_announcement(s, e, i)) 260 return; 261 262 if ((!(a = avahi_new(AvahiAnnouncement, 1)))) { 263 avahi_log_error(__FILE__": Out of memory."); 264 return; 265 } 266 267 a->server = s; 268 a->interface = i; 269 a->entry = e; 270 a->time_event = NULL; 271 272 AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_interface, i->announcements, a); 273 AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_entry, e->announcements, a); 274 275 go_to_initial_state(a, 0); 276 277/* avahi_log_debug("New announcement on interface %s.%i for entry [%s] state=%i", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record), a->state); */ 278/* avahi_free(t); */ 279} 280 281void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) { 282 AvahiEntry *e; 283 284 assert(s); 285 assert(i); 286 287 if (!i->announcing) 288 return; 289 290 for (e = s->entries; e; e = e->entries_next) 291 if (!e->dead) 292 new_announcement(s, i, e); 293} 294 295static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { 296 AvahiEntry *e = userdata; 297 298 assert(m); 299 assert(i); 300 assert(e); 301 assert(!e->dead); 302 303 new_announcement(m->server, i, e); 304} 305 306void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) { 307 assert(s); 308 assert(e); 309 assert(!e->dead); 310 311 avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e); 312} 313 314void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g) { 315 AvahiEntry *e; 316 317 assert(s); 318 assert(g); 319 320 for (e = g->entries; e; e = e->by_group_next) 321 if (!e->dead) 322 avahi_announce_entry(s, e); 323} 324 325int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { 326 AvahiAnnouncement *a; 327 328 assert(s); 329 assert(e); 330 assert(i); 331 assert(!e->dead); 332 333 if (!(a = avahi_get_announcement(s, e, i))) 334 return 0; 335 336 return 337 a->state == AVAHI_ANNOUNCING || 338 a->state == AVAHI_ESTABLISHED || 339 (a->state == AVAHI_WAITING && !(e->flags & AVAHI_ENTRY_UNIQUE)); 340} 341 342int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { 343 AvahiAnnouncement *a; 344 345 assert(s); 346 assert(e); 347 assert(i); 348 assert(!e->dead); 349 350 if (!(a = avahi_get_announcement(s, e, i))) 351 return 0; 352 353/* avahi_log_debug("state: %i", a->state); */ 354 355 return 356 a->state == AVAHI_PROBING || 357 (a->state == AVAHI_WAITING && (e->flags & AVAHI_ENTRY_UNIQUE)); 358} 359 360void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { 361 AvahiAnnouncement *a; 362 363 assert(s); 364 assert(e); 365 assert(i); 366 367 if (!(a = avahi_get_announcement(s, e, i))) 368 return; 369 370 if (a->state == AVAHI_PROBING && a->entry->group) 371 a->entry->group->n_probing--; 372 373 go_to_initial_state(a, 1); 374} 375 376static AvahiRecord *make_goodbye_record(AvahiRecord *r) { 377/* char *t; */ 378 AvahiRecord *g; 379 380 assert(r); 381 382/* avahi_log_debug("Preparing goodbye for record [%s]", t = avahi_record_to_string(r)); */ 383/* avahi_free(t); */ 384 385 if (!(g = avahi_record_copy(r))) 386 return NULL; /* OOM */ 387 388 assert(g->ref == 1); 389 g->ttl = 0; 390 391 return g; 392} 393 394static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { 395 AvahiEntry *e = userdata; 396 AvahiRecord *g; 397 398 assert(m); 399 assert(i); 400 assert(e); 401 assert(!e->dead); 402 403 if (!avahi_interface_match(i, e->interface, e->protocol)) 404 return; 405 406 if (e->flags & AVAHI_ENTRY_NOANNOUNCE) 407 return; 408 409 if (!avahi_entry_is_registered(m->server, e, i)) 410 return; 411 412 if (!(g = make_goodbye_record(e->record))) 413 return; /* OOM */ 414 415 avahi_interface_post_response(i, g, e->flags & AVAHI_ENTRY_UNIQUE, NULL, 1); 416 avahi_record_unref(g); 417} 418 419void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int goodbye) { 420 assert(s); 421 assert(i); 422 423/* avahi_log_debug("goodbye interface: %s.%u", i->hardware->name, i->protocol); */ 424 425 if (goodbye && avahi_interface_relevant(i)) { 426 AvahiEntry *e; 427 428 for (e = s->entries; e; e = e->entries_next) 429 if (!e->dead) 430 send_goodbye_callback(s->monitor, i, e); 431 } 432 433 while (i->announcements) 434 remove_announcement(s, i->announcements); 435 436/* avahi_log_debug("goodbye interface done: %s.%u", i->hardware->name, i->protocol); */ 437} 438 439void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int goodbye) { 440 assert(s); 441 assert(e); 442 443/* avahi_log_debug("goodbye entry: %p", e); */ 444 445 if (goodbye && !e->dead) 446 avahi_interface_monitor_walk(s->monitor, 0, AF_UNSPEC, send_goodbye_callback, e); 447 448 while (e->announcements) 449 remove_announcement(s, e->announcements); 450 451/* avahi_log_debug("goodbye entry done: %p", e); */ 452 453} 454 455void avahi_goodbye_all(AvahiServer *s, int goodbye) { 456 AvahiEntry *e; 457 458 assert(s); 459 460/* avahi_log_debug("goodbye all"); */ 461 462 for (e = s->entries; e; e = e->entries_next) 463 if (!e->dead) 464 avahi_goodbye_entry(s, e, goodbye); 465 466/* avahi_log_debug("goodbye all done"); */ 467 468} 469 470