]> git.meshlink.io Git - catta/blob - avahi-core/probe-sched.c
* implement reflection (including legacy unicast reflection)
[catta] / avahi-core / probe-sched.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 "probe-sched.h"
27 #include "util.h"
28
29 #define AVAHI_PROBE_HISTORY_MSEC 150
30 #define AVAHI_PROBE_DEFER_MSEC 50
31
32 typedef struct AvahiProbeJob AvahiProbeJob;
33
34 struct AvahiProbeJob {
35     AvahiProbeScheduler *scheduler;
36     AvahiTimeEvent *time_event;
37     
38     gboolean chosen; /* Use for packet assembling */
39     gboolean done;
40     GTimeVal delivery;
41
42     AvahiRecord *record;
43     
44     AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs);
45 };
46
47 struct AvahiProbeScheduler {
48     AvahiInterface *interface;
49     AvahiTimeEventQueue *time_event_queue;
50
51     AVAHI_LLIST_HEAD(AvahiProbeJob, jobs);
52     AVAHI_LLIST_HEAD(AvahiProbeJob, history);
53 };
54
55 static AvahiProbeJob* job_new(AvahiProbeScheduler *s, AvahiRecord *record, gboolean done) {
56     AvahiProbeJob *pj;
57     
58     g_assert(s);
59     g_assert(record);
60
61     pj = g_new(AvahiProbeJob, 1);
62     pj->scheduler = s;
63     pj->record = avahi_record_ref(record);
64     pj->time_event = NULL;
65     pj->chosen = FALSE;
66
67     if ((pj->done = done))
68         AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj);
69     else
70         AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->jobs, pj);
71
72     return pj;
73 }
74
75 static void job_free(AvahiProbeScheduler *s, AvahiProbeJob *pj) {
76     g_assert(pj);
77
78     if (pj->time_event)
79         avahi_time_event_queue_remove(s->time_event_queue, pj->time_event);
80
81     if (pj->done)
82         AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->history, pj);
83     else
84         AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj);
85
86     avahi_record_unref(pj->record);
87     g_free(pj);
88 }
89
90 static void elapse_callback(AvahiTimeEvent *e, gpointer data);
91
92 static void job_set_elapse_time(AvahiProbeScheduler *s, AvahiProbeJob *pj, guint msec, guint jitter) {
93     GTimeVal tv;
94
95     g_assert(s);
96     g_assert(pj);
97
98     avahi_elapse_time(&tv, msec, jitter);
99
100     if (pj->time_event)
101         avahi_time_event_queue_update(s->time_event_queue, pj->time_event, &tv);
102     else
103         pj->time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, elapse_callback, pj);
104 }
105
106 static void job_mark_done(AvahiProbeScheduler *s, AvahiProbeJob *pj) {
107     g_assert(s);
108     g_assert(pj);
109
110     g_assert(!pj->done);
111
112     AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj);
113     AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj);
114
115     pj->done = TRUE;
116
117     job_set_elapse_time(s, pj, AVAHI_PROBE_HISTORY_MSEC, 0);
118     g_get_current_time(&pj->delivery);
119 }
120
121 AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i) {
122     AvahiProbeScheduler *s;
123
124     g_assert(i);
125
126     s = g_new(AvahiProbeScheduler, 1);
127     s->interface = i;
128     s->time_event_queue = i->monitor->server->time_event_queue;
129
130     AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->jobs);
131     AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->history);
132     
133     return s;
134 }
135
136 void avahi_probe_scheduler_free(AvahiProbeScheduler *s) {
137     g_assert(s);
138
139     avahi_probe_scheduler_clear(s);
140     g_free(s);
141 }
142
143 void avahi_probe_scheduler_clear(AvahiProbeScheduler *s) {
144     g_assert(s);
145     
146     while (s->jobs)
147         job_free(s, s->jobs);
148     while (s->history)
149         job_free(s, s->history);
150 }
151  
152 static gboolean packet_add_probe_query(AvahiProbeScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) {
153     guint size;
154     AvahiKey *k;
155     gboolean b;
156
157     g_assert(s);
158     g_assert(p);
159     g_assert(pj);
160
161     g_assert(!pj->chosen);
162     
163     /* Estimate the size for this record */
164     size =
165         avahi_key_get_estimate_size(pj->record->key) +
166         avahi_record_get_estimate_size(pj->record);
167
168     /* Too large */
169     if (size > avahi_dns_packet_space(p))
170         return FALSE;
171
172     /* Create the probe query */
173     k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY);
174     b = !!avahi_dns_packet_append_key(p, k, FALSE);
175     g_assert(b);
176
177     /* Mark this job for addition to the packet */
178     pj->chosen = TRUE;
179
180     /* Scan for more jobs whith matching key pattern */
181     for (pj = s->jobs; pj; pj = pj->jobs_next) {
182         if (pj->chosen)
183             continue;
184
185         /* Does the record match the probe? */
186         if (k->class != pj->record->key->class || !avahi_domain_equal(k->name, pj->record->key->name))
187             continue;
188         
189         /* This job wouldn't fit in */
190         if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_space(p))
191             break;
192
193         /* Mark this job for addition to the packet */
194         pj->chosen = TRUE;
195     }
196
197     avahi_key_unref(k);
198             
199     return TRUE;
200 }
201
202 static void elapse_callback(AvahiTimeEvent *e, gpointer data) {
203     AvahiProbeJob *pj = data, *next;
204     AvahiProbeScheduler *s;
205     AvahiDnsPacket *p;
206     guint n;
207
208     g_assert(pj);
209     s = pj->scheduler;
210
211     if (pj->done) {
212         /* Lets remove it  from the history */
213         job_free(s, pj);
214         return;
215     }
216
217     p = avahi_dns_packet_new_query(s->interface->hardware->mtu);
218     n = 1;
219     
220     /* Add the import probe */
221     if (!packet_add_probe_query(s, p, pj)) {
222         guint size;
223         AvahiKey *k;
224         gboolean b;
225
226         avahi_dns_packet_free(p);
227
228         /* The probe didn't fit in the package, so let's allocate a larger one */
229
230         size =
231             avahi_key_get_estimate_size(pj->record->key) +
232             avahi_record_get_estimate_size(pj->record) +
233             AVAHI_DNS_PACKET_HEADER_SIZE;
234         
235         if (size > AVAHI_DNS_PACKET_MAX_SIZE)
236             size = AVAHI_DNS_PACKET_MAX_SIZE;
237         
238         p = avahi_dns_packet_new_query(size);
239
240         k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY);
241         b = avahi_dns_packet_append_key(p, k, FALSE) && avahi_dns_packet_append_record(p, pj->record, FALSE, 0);
242         avahi_key_unref(k);
243
244         if (b) {
245             avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, 1);
246             avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, 1);
247             avahi_interface_send_packet(s->interface, p);
248         } else
249             g_warning("Probe record too large, cannot send");   
250         
251         avahi_dns_packet_free(p);
252         job_mark_done(s, pj);
253
254         return;
255     }
256
257     /* Try to fill up packet with more probes, if available */
258     for (pj = s->jobs; pj; pj = pj->jobs_next) {
259
260         if (pj->chosen)
261             continue;
262         
263         if (!packet_add_probe_query(s, p, pj))
264             break;
265         
266         n++;
267     }
268
269     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);
270
271     n = 0;
272
273     /* Now add the chosen records to the authorative section */
274     for (pj = s->jobs; pj; pj = next) {
275
276         next = pj->jobs_next;
277
278         if (!pj->chosen)
279             continue;
280
281         if (!avahi_dns_packet_append_record(p, pj->record, FALSE, 0)) {
282             g_warning("Bad probe size estimate!");
283
284             /* Unmark all following jobs */
285             for (; pj; pj = pj->jobs_next)
286                 pj->chosen = FALSE;
287             
288             break;
289         }
290
291         job_mark_done(s, pj);
292         
293         n ++;
294     }
295     
296     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n);
297
298     /* Send it now */
299     avahi_interface_send_packet(s->interface, p);
300     avahi_dns_packet_free(p);
301 }
302
303 static AvahiProbeJob* find_scheduled_job(AvahiProbeScheduler *s, AvahiRecord *record) {
304     AvahiProbeJob *pj;
305
306     g_assert(s);
307     g_assert(record);
308
309     for (pj = s->jobs; pj; pj = pj->jobs_next) {
310         g_assert(!pj->done);
311         
312         if (avahi_record_equal_no_ttl(pj->record, record))
313             return pj;
314     }
315
316     return NULL;
317 }
318
319 static AvahiProbeJob* find_history_job(AvahiProbeScheduler *s, AvahiRecord *record) {
320     AvahiProbeJob *pj;
321     
322     g_assert(s);
323     g_assert(record);
324
325     for (pj = s->history; pj; pj = pj->jobs_next) {
326         g_assert(pj->done);
327
328         if (avahi_record_equal_no_ttl(pj->record, record)) {
329             /* Check whether this entry is outdated */
330
331             if (avahi_age(&pj->delivery) > AVAHI_PROBE_HISTORY_MSEC*1000) {
332                 /* it is outdated, so let's remove it */
333                 job_free(s, pj);
334                 return NULL;
335             }
336                 
337             return pj;
338         }
339     }
340
341     return NULL;
342 }
343
344 gboolean avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, gboolean immediately) {
345     AvahiProbeJob *pj;
346     GTimeVal tv;
347     
348     g_assert(s);
349     g_assert(record);
350     g_assert(!avahi_key_is_pattern(record->key));
351
352     if ((pj = find_history_job(s, record)))
353         return FALSE;
354     
355     avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0);
356
357     if ((pj = find_scheduled_job(s, record))) {
358
359         if (avahi_timeval_compare(&tv, &pj->delivery) < 0) {
360             /* If the new entry should be scheduled earlier, update the old entry */
361             pj->delivery = tv;
362             avahi_time_event_queue_update(s->time_event_queue, pj->time_event, &pj->delivery);
363         }
364
365         return TRUE;
366     } else {
367         /* Create a new job and schedule it */
368         pj = job_new(s, record, FALSE);
369         pj->delivery = tv;
370         pj->time_event = avahi_time_event_queue_add(s->time_event_queue, &pj->delivery, elapse_callback, pj);
371
372         
373 /*     g_message("Accepted new probe job."); */
374
375         return TRUE;
376     }
377 }