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