]> git.meshlink.io Git - catta/blob - avahi-core/announce.c
* implement record updating through the new flags AVAHI_PUBLISH_UPDATE
[catta] / avahi-core / announce.c
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
36 static 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
49 static void elapse_announce(AvahiTimeEvent *e, void *userdata);
50
51 static 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
68 static void next_state(AvahiAnnouncement *a);
69
70 void 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
110 static 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_PUBLISH_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
188 static void elapse_announce(AvahiTimeEvent *e, void *userdata) {
189     assert(e);
190
191     next_state(userdata);
192 }
193
194 AvahiAnnouncement *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
208 static 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_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE))
216         a->state = AVAHI_PROBING;
217     else if (!(e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)) {
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         set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC));
235     else if (a->state == AVAHI_ANNOUNCING) 
236         set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC));
237     else
238         set_timeout(a, NULL);
239 }
240
241 static void new_announcement(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) {
242     AvahiAnnouncement *a;
243 /*     char *t;  */
244
245     assert(s);
246     assert(i);
247     assert(e);
248     assert(!e->dead);
249
250 /*     avahi_log_debug("NEW ANNOUNCEMENT: %s.%i [%s]", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record)); */
251 /*     avahi_free(t); */
252     
253     if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_is_commited(e))
254         return;
255
256     /* We don't want duplicate announcements */
257     if (avahi_get_announcement(s, e, i))
258         return;    
259
260     if ((!(a = avahi_new(AvahiAnnouncement, 1)))) {
261         avahi_log_error(__FILE__": Out of memory.");
262         return;
263     }
264     
265     a->server = s;
266     a->interface = i;
267     a->entry = e;
268     a->time_event = NULL;
269
270     AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_interface, i->announcements, a);
271     AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_entry, e->announcements, a);
272
273     go_to_initial_state(a, 0);
274     
275 /*     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); */
276 /*     avahi_free(t); */
277 }
278
279 void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) {
280     AvahiEntry *e;
281     
282     assert(s);
283     assert(i);
284
285     if (!i->announcing)
286         return;
287
288     for (e = s->entries; e; e = e->entries_next)
289         if (!e->dead)
290             new_announcement(s, i, e);
291 }
292
293 static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
294     AvahiEntry *e = userdata;
295     
296     assert(m);
297     assert(i);
298     assert(e);
299     assert(!e->dead);
300
301     new_announcement(m->server, i, e);
302 }
303
304 void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) {
305     assert(s);
306     assert(e);
307     assert(!e->dead);
308
309     avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e);
310 }
311
312 void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g) {
313     AvahiEntry *e;
314     
315     assert(s);
316     assert(g);
317
318     for (e = g->entries; e; e = e->by_group_next)
319         if (!e->dead)
320             avahi_announce_entry(s, e);
321 }
322
323 int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
324     AvahiAnnouncement *a;
325
326     assert(s);
327     assert(e);
328     assert(i);
329     assert(!e->dead);
330
331     if (!(a = avahi_get_announcement(s, e, i)))
332         return 0;
333     
334     return
335         a->state == AVAHI_ANNOUNCING ||
336         a->state == AVAHI_ESTABLISHED ||
337         (a->state == AVAHI_WAITING && !(e->flags & AVAHI_PUBLISH_UNIQUE));
338 }
339
340 int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
341     AvahiAnnouncement *a;
342
343     assert(s);
344     assert(e);
345     assert(i);
346     assert(!e->dead);
347
348     if (!(a = avahi_get_announcement(s, e, i)))
349         return 0;
350
351 /*     avahi_log_debug("state: %i", a->state); */
352     
353     return
354         a->state == AVAHI_PROBING ||
355         (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE));
356 }
357
358 void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
359     AvahiAnnouncement *a;
360     
361     assert(s);
362     assert(e);
363     assert(i);
364
365     if (!(a = avahi_get_announcement(s, e, i)))
366         return;
367
368     if (a->state == AVAHI_PROBING && a->entry->group)
369         a->entry->group->n_probing--;
370
371     go_to_initial_state(a, 1);
372 }
373
374 static AvahiRecord *make_goodbye_record(AvahiRecord *r) {
375 /*     char *t; */
376     AvahiRecord *g;
377     
378     assert(r);
379
380 /*     avahi_log_debug("Preparing goodbye for record [%s]", t = avahi_record_to_string(r)); */
381 /*     avahi_free(t); */
382
383     if (!(g = avahi_record_copy(r)))
384         return NULL; /* OOM */
385     
386     assert(g->ref == 1);
387     g->ttl = 0;
388
389     return g;
390 }
391
392 static int is_duplicate_entry(AvahiServer *s, AvahiEntry *e) {
393     AvahiEntry *i;
394     
395     assert(s);
396     assert(e);
397
398     for (i = avahi_hashmap_lookup(s->entries_by_key, e->record->key); i; i = i->by_key_next) {
399
400         if (i == e)
401             continue;
402
403         if (!avahi_record_equal_no_ttl(i->record, e->record))
404             continue;
405         
406         return 1;
407     }
408
409     return 0;
410 }
411
412 static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
413     AvahiEntry *e = userdata;
414     AvahiRecord *g;
415     
416     assert(m);
417     assert(i);
418     assert(e);
419     assert(!e->dead);
420
421     if (!avahi_interface_match(i, e->interface, e->protocol))
422         return;
423
424     if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)
425         return;
426
427     if (!avahi_entry_is_registered(m->server, e, i))
428         return;
429
430     if (is_duplicate_entry(m->server, e))
431         return;
432     
433     if (!(g = make_goodbye_record(e->record)))
434         return; /* OOM */
435     
436     avahi_interface_post_response(i, g, e->flags & AVAHI_PUBLISH_UNIQUE, NULL, 1);
437     avahi_record_unref(g);
438 }
439
440 static void reannounce(AvahiAnnouncement *a) {
441     AvahiEntry *e;
442     struct timeval tv;
443         
444     assert(a);
445     e = a->entry;
446
447     /* If the group this entry belongs to is not even commited, there's nothing to reannounce */
448     if (e->group && (e->group->state == AVAHI_ENTRY_GROUP_UNCOMMITED || e->group->state == AVAHI_ENTRY_GROUP_COLLISION))
449         return;
450
451     /* Because we might change state we decrease the probing counter first */
452     if (a->state == AVAHI_PROBING && a->entry->group)
453         a->entry->group->n_probing--;
454     
455     if (a->state == AVAHI_PROBING ||
456         (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE)))
457
458         /* We were probing or waiting after probe, so we restart probing from the beginning here */
459         
460         a->state = AVAHI_PROBING;
461     else if (a->state == AVAHI_WAITING)
462
463         /* We were waiting, but were not probing before, so we continue waiting  */
464         a->state = AVAHI_WAITING;
465
466     else if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)
467         
468         /* No announcement needed */
469         a->state = AVAHI_ESTABLISHED;
470
471     else {
472         
473         /* Ok, let's restart announcing */
474         a->state = AVAHI_ANNOUNCING;
475     } 
476         
477     /* Now let's increase the probing counter again */
478     if (a->state == AVAHI_PROBING && e->group)
479         e->group->n_probing++;
480     
481     a->n_iteration = 1;
482     a->sec_delay = 1;
483
484     if (a->state == AVAHI_PROBING)
485         set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC));
486     else if (a->state == AVAHI_ANNOUNCING) 
487         set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC));
488     else
489         set_timeout(a, NULL);
490 }
491
492
493 static void reannounce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
494     AvahiEntry *e = userdata;
495     AvahiAnnouncement *a;
496     
497     assert(m);
498     assert(i);
499     assert(e);
500     assert(!e->dead);
501
502     if (!(a = avahi_get_announcement(m->server, e, i)))
503         return;
504
505     reannounce(a);
506 }
507
508 void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e) {
509
510     assert(s);
511     assert(e);
512     assert(!e->dead);
513
514     avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, reannounce_walk_callback, e);
515 }
516     
517 void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int remove) {
518     assert(s);
519     assert(i);
520
521     if (send_goodbye)
522         if (avahi_interface_is_relevant(i)) {
523             AvahiEntry *e;
524             
525             for (e = s->entries; e; e = e->entries_next)
526                 if (!e->dead)
527                     send_goodbye_callback(s->monitor, i, e);
528         }
529
530     if (remove)
531         while (i->announcements)
532             remove_announcement(s, i->announcements);
533 }
534
535 void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int remove) {
536     assert(s);
537     assert(e);
538     
539     if (send_goodbye)
540         if (!e->dead)
541             avahi_interface_monitor_walk(s->monitor, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, send_goodbye_callback, e);
542
543     if (remove)
544         while (e->announcements)
545             remove_announcement(s, e->announcements);
546 }
547
548 void avahi_goodbye_all(AvahiServer *s, int send_goodbye, int remove) {
549     AvahiEntry *e;
550     
551     assert(s);
552
553     for (e = s->entries; e; e = e->entries_next)
554         if (!e->dead)
555             avahi_goodbye_entry(s, e, send_goodbye, remove);
556
557 }
558