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