]> git.meshlink.io Git - catta/blob - avahi-core/announce.c
implement new main loop abstraction layer
[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 "announce.h"
28
29 #define AVAHI_ANNOUNCEMENT_JITTER_MSEC 250
30 #define AVAHI_PROBE_JITTER_MSEC 250
31 #define AVAHI_PROBE_INTERVAL_MSEC 250
32
33 static void remove_announcement(AvahiServer *s, AvahiAnnouncement *a) {
34     g_assert(s);
35     g_assert(a);
36
37     if (a->time_event)
38         avahi_time_event_queue_remove(s->time_event_queue, a->time_event);
39
40     AVAHI_LLIST_REMOVE(AvahiAnnouncement, by_interface, a->interface->announcements, a);
41     AVAHI_LLIST_REMOVE(AvahiAnnouncement, by_entry, a->entry->announcements, a);
42     
43     g_free(a);
44 }
45
46 static void elapse_announce(AvahiTimeEvent *e, void *userdata);
47
48 static void set_timeout(AvahiAnnouncement *a, const struct timeval *tv) {
49     g_assert(a);
50
51     if (!tv) {
52         if (a->time_event) {
53             avahi_time_event_queue_remove(a->server->time_event_queue, a->time_event);
54             a->time_event = NULL;
55         }
56     } else {
57
58         if (a->time_event) 
59             avahi_time_event_queue_update(a->server->time_event_queue, a->time_event, tv);
60         else
61             a->time_event = avahi_time_event_queue_add(a->server->time_event_queue, tv, elapse_announce, a);
62     }
63 }
64
65 static void next_state(AvahiAnnouncement *a);
66
67 void avahi_entry_group_check_probed(AvahiEntryGroup *g, gboolean immediately) {
68     AvahiEntry *e;
69     g_assert(g);
70     g_assert(!g->dead);
71
72     /* Check whether all group members have been probed */
73     
74     if (g->state != AVAHI_ENTRY_GROUP_REGISTERING || g->n_probing > 0) 
75         return;
76
77     avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_ESTABLISHED);
78
79     if (g->dead)
80         return;
81     
82     for (e = g->entries; e; e = e->entries_next) {
83         AvahiAnnouncement *a;
84         
85         for (a = e->announcements; a; a = a->by_entry_next) {
86
87             if (a->state != AVAHI_WAITING)
88                 continue;
89             
90             a->state = AVAHI_ANNOUNCING;
91
92             if (immediately) {
93                 /* Shortcut */
94                 
95                 a->n_iteration = 1;
96                 next_state(a);
97             } else {
98                 struct timeval tv;
99                 a->n_iteration = 0;
100                 avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC);
101                 set_timeout(a, &tv);
102             }
103         }
104     }
105 }
106
107 static void next_state(AvahiAnnouncement *a) {
108     g_assert(a);
109
110 /*     avahi_log_debug("%i -- %u", a->state, a->n_iteration);   */
111     
112     if (a->state == AVAHI_WAITING) {
113
114         g_assert(a->entry->group);
115
116         avahi_entry_group_check_probed(a->entry->group, TRUE);
117         
118     } else if (a->state == AVAHI_PROBING) {
119
120         if (a->n_iteration >= 4) {
121             /* Probing done */
122             
123 /*             gchar *t; */
124
125 /*             avahi_log_debug("Enough probes for record [%s]", t = avahi_record_to_string(a->entry->record)); */
126 /*             g_free(t); */
127
128             if (a->entry->group) {
129                 g_assert(a->entry->group->n_probing);
130                 a->entry->group->n_probing--;
131             }
132             
133             if (a->entry->group && a->entry->group->state == AVAHI_ENTRY_GROUP_REGISTERING)
134                 a->state = AVAHI_WAITING;
135             else {
136                 a->state = AVAHI_ANNOUNCING;
137                 a->n_iteration = 1;
138             }
139
140             set_timeout(a, NULL);
141             next_state(a);
142         } else {
143             struct timeval tv;
144
145             avahi_interface_post_probe(a->interface, a->entry->record, FALSE);
146             
147             avahi_elapse_time(&tv, AVAHI_PROBE_INTERVAL_MSEC, 0);
148             set_timeout(a, &tv);
149             
150             a->n_iteration++;
151         }
152
153     } else if (a->state == AVAHI_ANNOUNCING) {
154
155         if (a->entry->flags & AVAHI_ENTRY_UNIQUE)
156             /* Send the whole rrset at once */
157             avahi_server_prepare_matching_responses(a->server, a->interface, a->entry->record->key, FALSE);
158         else
159             avahi_server_prepare_response(a->server, a->interface, a->entry, FALSE, FALSE);
160
161         avahi_server_generate_response(a->server, a->interface, NULL, NULL, 0, FALSE, FALSE);
162
163         if (++a->n_iteration >= 4) {
164 /*             gchar *t; */
165             /* Announcing done */
166
167 /*             avahi_log_debug("Enough announcements for record [%s]", t = avahi_record_to_string(a->entry->record)); */
168 /*             g_free(t); */
169
170             a->state = AVAHI_ESTABLISHED;
171
172             set_timeout(a, NULL);
173         } else {
174             struct timeval tv;
175             avahi_elapse_time(&tv, a->sec_delay*1000, AVAHI_ANNOUNCEMENT_JITTER_MSEC);
176         
177             if (a->n_iteration < 10)
178                 a->sec_delay *= 2;
179             
180             set_timeout(a, &tv);
181         }
182     }
183 }
184
185 static void elapse_announce(AvahiTimeEvent *e, void *userdata) {
186     g_assert(e);
187
188     next_state(userdata);
189 }
190
191 AvahiAnnouncement *avahi_get_announcement(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
192     AvahiAnnouncement *a;
193     
194     g_assert(s);
195     g_assert(e);
196     g_assert(i);
197
198     for (a = e->announcements; a; a = a->by_entry_next)
199         if (a->interface == i)
200             return a;
201
202     return NULL;
203 }
204
205 static void go_to_initial_state(AvahiAnnouncement *a, gboolean immediately) {
206     AvahiEntry *e;
207     struct timeval tv;
208         
209     g_assert(a);
210     e = a->entry;
211
212     if ((e->flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_NOPROBE))
213         a->state = AVAHI_PROBING;
214     else if (!(e->flags & AVAHI_ENTRY_NOANNOUNCE)) {
215
216         if (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED)
217             a->state = AVAHI_ANNOUNCING;
218         else
219             a->state = AVAHI_WAITING;
220         
221     } else
222         a->state = AVAHI_ESTABLISHED;
223
224     a->n_iteration = 1;
225     a->sec_delay = 1;
226
227     if (a->state == AVAHI_PROBING && e->group)
228         e->group->n_probing++;
229
230     if (a->state == AVAHI_PROBING) {
231         avahi_elapse_time(&tv, 0, immediately ? 0 : AVAHI_PROBE_JITTER_MSEC);
232         set_timeout(a, &tv);
233     } else if (a->state == AVAHI_ANNOUNCING) {
234         avahi_elapse_time(&tv, 0, immediately ? 0 : AVAHI_ANNOUNCEMENT_JITTER_MSEC);
235         set_timeout(a, &tv);
236     } else
237         set_timeout(a, NULL);
238 }
239
240 static void new_announcement(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) {
241     AvahiAnnouncement *a;
242 /*     gchar *t;  */
243
244     g_assert(s);
245     g_assert(i);
246     g_assert(e);
247     g_assert(!e->dead);
248
249 /*     avahi_log_debug("NEW ANNOUNCEMENT: %s.%i [%s]", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record)); */
250 /*     g_free(t); */
251     
252     if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_commited(e))
253         return;
254
255     /* We don't want duplicate announcements */
256     if (avahi_get_announcement(s, e, i))
257         return;    
258
259     a = g_new(AvahiAnnouncement, 1);
260     a->server = s;
261     a->interface = i;
262     a->entry = e;
263     a->time_event = NULL;
264
265     AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_interface, i->announcements, a);
266     AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_entry, e->announcements, a);
267
268     go_to_initial_state(a, FALSE);
269     
270 /*     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); */
271 /*     g_free(t); */
272 }
273
274 void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) {
275     AvahiEntry *e;
276     
277     g_assert(s);
278     g_assert(i);
279
280     if (!i->announcing)
281         return;
282
283     for (e = s->entries; e; e = e->entries_next)
284         if (!e->dead)
285             new_announcement(s, i, e);
286 }
287
288 static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
289     AvahiEntry *e = userdata;
290     
291     g_assert(m);
292     g_assert(i);
293     g_assert(e);
294     g_assert(!e->dead);
295
296     new_announcement(m->server, i, e);
297 }
298
299 void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) {
300     g_assert(s);
301     g_assert(e);
302     g_assert(!e->dead);
303
304     avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e);
305 }
306
307 void avahi_announce_group(AvahiServer *s, AvahiEntryGroup *g) {
308     AvahiEntry *e;
309     
310     g_assert(s);
311     g_assert(g);
312
313     for (e = g->entries; e; e = e->by_group_next)
314         if (!e->dead)
315             avahi_announce_entry(s, e);
316 }
317
318 gboolean avahi_entry_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
319     AvahiAnnouncement *a;
320
321     g_assert(s);
322     g_assert(e);
323     g_assert(i);
324     g_assert(!e->dead);
325
326     if (!(a = avahi_get_announcement(s, e, i)))
327         return FALSE;
328     
329     return
330         a->state == AVAHI_ANNOUNCING ||
331         a->state == AVAHI_ESTABLISHED ||
332         (a->state == AVAHI_WAITING && !(e->flags & AVAHI_ENTRY_UNIQUE));
333 }
334
335 gboolean avahi_entry_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
336     AvahiAnnouncement *a;
337
338     g_assert(s);
339     g_assert(e);
340     g_assert(i);
341     g_assert(!e->dead);
342
343     if (!(a = avahi_get_announcement(s, e, i)))
344         return FALSE;
345
346 /*     avahi_log_debug("state: %i", a->state); */
347     
348     return
349         a->state == AVAHI_PROBING ||
350         (a->state == AVAHI_WAITING && (e->flags & AVAHI_ENTRY_UNIQUE));
351 }
352
353 void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
354     AvahiAnnouncement *a;
355     
356     g_assert(s);
357     g_assert(e);
358     g_assert(i);
359
360     if (!(a = avahi_get_announcement(s, e, i)))
361         return;
362
363     if (a->state == AVAHI_PROBING && a->entry->group)
364         a->entry->group->n_probing--;
365
366     go_to_initial_state(a, TRUE);
367 }
368
369 static AvahiRecord *make_goodbye_record(AvahiRecord *r) {
370 /*     gchar *t; */
371     AvahiRecord *g;
372     
373     g_assert(r);
374
375 /*     avahi_log_debug("Preparing goodbye for record [%s]", t = avahi_record_to_string(r)); */
376 /*     g_free(t); */
377
378     g = avahi_record_copy(r);
379     g_assert(g->ref == 1);
380     g->ttl = 0;
381
382     return g;
383 }
384
385 static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
386     AvahiEntry *e = userdata;
387     AvahiRecord *g;
388     
389     g_assert(m);
390     g_assert(i);
391     g_assert(e);
392     g_assert(!e->dead);
393
394     if (!avahi_interface_match(i, e->interface, e->protocol))
395         return;
396
397     if (e->flags & AVAHI_ENTRY_NOANNOUNCE)
398         return;
399
400     if (!avahi_entry_registered(m->server, e, i))
401         return;
402     
403     g = make_goodbye_record(e->record);
404     avahi_interface_post_response(i, g, e->flags & AVAHI_ENTRY_UNIQUE, NULL, TRUE);
405     avahi_record_unref(g);
406 }
407     
408 void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, gboolean goodbye) {
409     g_assert(s);
410     g_assert(i);
411
412 /*     avahi_log_debug("goodbye interface: %s.%u", i->hardware->name, i->protocol); */
413
414     if (goodbye && avahi_interface_relevant(i)) {
415         AvahiEntry *e;
416         
417         for (e = s->entries; e; e = e->entries_next)
418             if (!e->dead)
419                 send_goodbye_callback(s->monitor, i, e);
420     }
421
422     while (i->announcements)
423         remove_announcement(s, i->announcements);
424
425 /*     avahi_log_debug("goodbye interface done: %s.%u", i->hardware->name, i->protocol); */
426
427 }
428
429 void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, gboolean goodbye) {
430     g_assert(s);
431     g_assert(e);
432     
433 /*     avahi_log_debug("goodbye entry: %p", e); */
434     
435     if (goodbye && !e->dead)
436         avahi_interface_monitor_walk(s->monitor, 0, AF_UNSPEC, send_goodbye_callback, e);
437
438     while (e->announcements)
439         remove_announcement(s, e->announcements);
440
441 /*     avahi_log_debug("goodbye entry done: %p", e); */
442
443 }
444
445 void avahi_goodbye_all(AvahiServer *s, gboolean goodbye) {
446     AvahiEntry *e;
447     
448     g_assert(s);
449
450 /*     avahi_log_debug("goodbye all"); */
451
452     for (e = s->entries; e; e = e->entries_next)
453         if (!e->dead)
454             avahi_goodbye_entry(s, e, goodbye);
455
456 /*     avahi_log_debug("goodbye all done"); */
457
458 }
459