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