]> git.meshlink.io Git - catta/blob - avahi-core/probe-sched.c
* split packet scheduler into three seperate parts
[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_DEFER_MSEC 70
30
31 typedef struct AvahiProbeJob AvahiProbeJob;
32
33 struct AvahiProbeJob {
34     AvahiProbeScheduler *scheduler;
35     AvahiTimeEvent *time_event;
36     
37     gboolean chosen; /* Use for packet assembling */
38     GTimeVal delivery;
39
40     AvahiRecord *record;
41     
42     AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs);
43 };
44
45 struct AvahiProbeScheduler {
46     AvahiInterface *interface;
47     AvahiTimeEventQueue *time_event_queue;
48
49     AVAHI_LLIST_HEAD(AvahiProbeJob, jobs);
50 };
51
52 static AvahiProbeJob* job_new(AvahiProbeScheduler *s, AvahiRecord *record) {
53     AvahiProbeJob *pj;
54     
55     g_assert(s);
56     g_assert(record);
57
58     pj = g_new(AvahiProbeJob, 1);
59     pj->scheduler = s;
60     pj->record = avahi_record_ref(record);
61     pj->time_event = NULL;
62     pj->chosen = FALSE;
63     
64     AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->jobs, pj);
65
66     return pj;
67 }
68
69 static void job_free(AvahiProbeScheduler *s, AvahiProbeJob *pj) {
70     g_assert(pj);
71
72     if (pj->time_event)
73         avahi_time_event_queue_remove(s->time_event_queue, pj->time_event);
74
75     AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj);
76
77     avahi_record_unref(pj->record);
78     g_free(pj);
79 }
80
81
82 AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i) {
83     AvahiProbeScheduler *s;
84
85     g_assert(i);
86
87     s = g_new(AvahiProbeScheduler, 1);
88     s->interface = i;
89     s->time_event_queue = i->monitor->server->time_event_queue;
90
91     AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->jobs);
92     
93     return s;
94 }
95
96 void avahi_probe_scheduler_free(AvahiProbeScheduler *s) {
97     g_assert(s);
98
99     avahi_probe_scheduler_clear(s);
100     g_free(s);
101 }
102
103 void avahi_probe_scheduler_clear(AvahiProbeScheduler *s) {
104     g_assert(s);
105     
106     while (s->jobs)
107         job_free(s, s->jobs);
108 }
109  
110 static gboolean packet_add_probe_query(AvahiProbeScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) {
111     guint size;
112     AvahiKey *k;
113     gboolean b;
114
115     g_assert(s);
116     g_assert(p);
117     g_assert(pj);
118
119     g_assert(!pj->chosen);
120     
121     /* Estimate the size for this record */
122     size =
123         avahi_key_get_estimate_size(pj->record->key) +
124         avahi_record_get_estimate_size(pj->record);
125
126     /* Too large */
127     if (size > avahi_dns_packet_space(p))
128         return FALSE;
129
130     /* Create the probe query */
131     k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY);
132     b = !!avahi_dns_packet_append_key(p, k, FALSE);
133     g_assert(b);
134
135     /* Mark this job for addition to the packet */
136     pj->chosen = TRUE;
137
138     /* Scan for more jobs whith matching key pattern */
139     for (pj = s->jobs; pj; pj = pj->jobs_next) {
140         if (pj->chosen)
141             continue;
142
143         /* Does the record match the probe? */
144         if (k->class != pj->record->key->class || !avahi_domain_equal(k->name, pj->record->key->name))
145             continue;
146         
147         /* This job wouldn't fit in */
148         if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_space(p))
149             break;
150
151         /* Mark this job for addition to the packet */
152         pj->chosen = TRUE;
153     }
154
155     avahi_key_unref(k);
156             
157     return TRUE;
158 }
159
160 static void elapse_callback(AvahiTimeEvent *e, gpointer data) {
161     AvahiProbeJob *pj = data, *next;
162     AvahiProbeScheduler *s;
163     AvahiDnsPacket *p;
164     guint n;
165
166     g_assert(pj);
167     s = pj->scheduler;
168
169     p = avahi_dns_packet_new_query(s->interface->hardware->mtu);
170     n = 1;
171     
172     /* Add the import probe */
173     if (!packet_add_probe_query(s, p, pj)) {
174         guint size;
175         AvahiKey *k;
176         gboolean b;
177
178         avahi_dns_packet_free(p);
179
180         /* The probe didn't fit in the package, so let's allocate a larger one */
181
182         size =
183             avahi_key_get_estimate_size(pj->record->key) +
184             avahi_record_get_estimate_size(pj->record) +
185             AVAHI_DNS_PACKET_HEADER_SIZE;
186         
187         if (size > AVAHI_DNS_PACKET_MAX_SIZE)
188             size = AVAHI_DNS_PACKET_MAX_SIZE;
189         
190         p = avahi_dns_packet_new_query(size);
191
192         k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY);
193         b = avahi_dns_packet_append_key(p, k, FALSE) && avahi_dns_packet_append_record(p, pj->record, FALSE, 0);
194         avahi_key_unref(k);
195
196         if (b) {
197             avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, 1);
198             avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, 1);
199             avahi_interface_send_packet(s->interface, p);
200         } else
201             g_warning("Probe record too large, cannot send");   
202         
203         avahi_dns_packet_free(p);
204         job_free(s, pj);
205
206         return;
207     }
208
209     /* Try to fill up packet with more probes, if available */
210     for (pj = s->jobs; pj; pj = pj->jobs_next) {
211
212         if (pj->chosen)
213             continue;
214         
215         if (!packet_add_probe_query(s, p, pj))
216             break;
217         
218         n++;
219     }
220
221     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);
222
223     n = 0;
224
225     /* Now add the chosen records to the authorative section */
226     for (pj = s->jobs; pj; pj = next) {
227
228         next = pj->jobs_next;
229
230         if (!pj->chosen)
231             continue;
232
233         if (!avahi_dns_packet_append_record(p, pj->record, FALSE, 0)) {
234             g_warning("Bad probe size estimate!");
235
236             /* Unmark all following jobs */
237             for (; pj; pj = pj->jobs_next)
238                 pj->chosen = FALSE;
239             
240             break;
241         }
242
243         job_free(s, pj);
244         
245         n ++;
246     }
247     
248     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n);
249
250     /* Send it now */
251     avahi_interface_send_packet(s->interface, p);
252     avahi_dns_packet_free(p);
253 }
254
255 gboolean avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, gboolean immediately) {
256     AvahiProbeJob *pj;
257     GTimeVal tv;
258     
259     g_assert(s);
260     g_assert(record);
261     g_assert(!avahi_key_is_pattern(record->key));
262     
263     avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0);
264
265     /* Create a new job and schedule it */
266     pj = job_new(s, record);
267     pj->delivery = tv;
268     pj->time_event = avahi_time_event_queue_add(s->time_event_queue, &pj->delivery, elapse_callback, pj);
269
270     g_message("Accepted new probe job.");
271
272     return TRUE;
273 }