4 This file is part of avahi.
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.
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.
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
27 static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) {
33 /* g_message("removing from cache: %p %p", c, e); */
35 /* Remove from hash table */
36 t = g_hash_table_lookup(c->hash_table, e->record->key);
37 AVAHI_LLIST_REMOVE(AvahiCacheEntry, by_key, t, e);
39 g_hash_table_replace(c->hash_table, t->record->key, t);
41 g_hash_table_remove(c->hash_table, e->record->key);
43 /* Remove from linked list */
44 AVAHI_LLIST_REMOVE(AvahiCacheEntry, entry, c->entries, e);
47 avahi_time_event_queue_remove(c->server->time_event_queue, e->time_event);
49 avahi_subscription_notify(c->server, c->interface, e->record, AVAHI_SUBSCRIPTION_REMOVE);
51 avahi_record_unref(e->record);
56 AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *iface) {
60 c = g_new(AvahiCache, 1);
63 c->hash_table = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
65 AVAHI_LLIST_HEAD_INIT(AvahiCacheEntry, c->entries);
70 void avahi_cache_free(AvahiCache *c) {
74 remove_entry(c, c->entries);
76 g_hash_table_destroy(c->hash_table);
81 AvahiCacheEntry *avahi_cache_lookup_key(AvahiCache *c, AvahiKey *k) {
85 g_assert(!avahi_key_is_pattern(k));
87 return g_hash_table_lookup(c->hash_table, k);
90 gpointer avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, gpointer userdata) {
97 if (avahi_key_is_pattern(pattern)) {
98 AvahiCacheEntry *e, *n;
100 for (e = c->entries; e; e = n) {
103 if (avahi_key_pattern_match(pattern, e->record->key))
104 if ((ret = cb(c, pattern, e, userdata)))
109 AvahiCacheEntry *e, *n;
111 for (e = avahi_cache_lookup_key(c, pattern); e; e = n) {
114 if ((ret = cb(c, pattern, e, userdata)))
122 static gpointer lookup_record_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) {
127 if (avahi_record_equal_no_ttl(e->record, userdata))
133 AvahiCacheEntry *avahi_cache_lookup_record(AvahiCache *c, AvahiRecord *r) {
137 return avahi_cache_walk(c, r->key, lookup_record_callback, r);
140 static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, guint percent);
142 static void elapse_func(AvahiTimeEvent *t, void *userdata) {
143 AvahiCacheEntry *e = userdata;
148 if (e->state == AVAHI_CACHE_FINAL) {
149 remove_entry(e->cache, e);
150 g_message("Removing entry from cache due to expiration");
155 case AVAHI_CACHE_VALID:
156 e->state = AVAHI_CACHE_EXPIRY1;
160 case AVAHI_CACHE_EXPIRY1:
161 e->state = AVAHI_CACHE_EXPIRY2;
164 case AVAHI_CACHE_EXPIRY2:
165 e->state = AVAHI_CACHE_EXPIRY3;
169 case AVAHI_CACHE_EXPIRY3:
170 e->state = AVAHI_CACHE_FINAL;
178 g_assert(percent > 0);
180 g_message("Requesting cache entry update at %i%%.", percent);
182 /* Request a cache update, if we are subscribed to this entry */
183 if (avahi_is_subscribed(e->cache->server, e->record->key))
184 avahi_interface_post_query(e->cache->interface, e->record->key, TRUE);
186 /* Check again later */
187 next_expiry(e->cache, e, percent);
191 static void update_time_event(AvahiCache *c, AvahiCacheEntry *e) {
196 avahi_time_event_queue_update(c->server->time_event_queue, e->time_event, &e->expiry);
198 e->time_event = avahi_time_event_queue_add(c->server->time_event_queue, &e->expiry, elapse_func, e);
201 static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, guint percent) {
206 g_assert(percent > 0 && percent <= 100);
208 e->expiry = e->timestamp;
210 usec = e->record->ttl * 10000;
213 usec = g_random_int_range(usec*percent, usec*(percent+2));
215 g_time_val_add(&e->expiry, usec);
216 update_time_event(c, e);
219 void avahi_cache_update(AvahiCache *c, AvahiRecord *r, gboolean unique, const AvahiAddress *a) {
220 AvahiCacheEntry *e, *t;
224 g_assert(r && r->ref >= 1);
226 g_message("cache update: %s", (txt = avahi_record_to_string(r)));
231 /* This is a goodbye request */
233 if ((e = avahi_cache_lookup_record(c, r))) {
235 e->state = AVAHI_CACHE_FINAL;
236 g_get_current_time(&e->timestamp);
237 e->expiry = e->timestamp;
238 g_time_val_add(&e->expiry, 1000000); /* 1s */
239 update_time_event(c, e);
244 /* This is an update request */
246 if ((t = e = avahi_cache_lookup_key(c, r->key))) {
250 /* For unique records, remove all entries but one */
251 while (e->by_key_next)
252 remove_entry(c, e->by_key_next);
256 /* For non-unique record, look for exactly the same entry */
257 for (; e; e = e->by_key_next)
258 if (avahi_record_equal_no_ttl(e->record, r))
265 /* g_message("found matching cache entry"); */
267 /* We are the first in the linked list so let's replace the hash table key with the new one */
268 if (e->by_key_prev == NULL)
269 g_hash_table_replace(c->hash_table, r->key, e);
271 /* Notify subscribers */
272 if (!avahi_record_equal_no_ttl(e->record, r))
273 avahi_subscription_notify(c->server, c->interface, r, AVAHI_SUBSCRIPTION_CHANGE);
275 /* Update the record */
276 avahi_record_unref(e->record);
277 e->record = avahi_record_ref(r);
280 /* No entry found, therefore we create a new one */
282 /* g_message("couldn't find matching cache entry"); */
284 e = g_new(AvahiCacheEntry, 1);
286 e->time_event = NULL;
287 e->record = avahi_record_ref(r);
289 /* Append to hash table */
290 AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, t, e);
291 g_hash_table_replace(c->hash_table, e->record->key, t);
293 /* Append to linked list */
294 AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e);
296 /* Notify subscribers */
297 avahi_subscription_notify(c->server, c->interface, e->record, AVAHI_SUBSCRIPTION_NEW);
301 g_get_current_time(&e->timestamp);
302 next_expiry(c, e, 80);
303 e->state = AVAHI_CACHE_VALID;
307 static gpointer drop_key_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
316 void avahi_cache_drop_key(AvahiCache *c, AvahiKey *k) {
320 avahi_cache_walk(c, k, drop_key_callback, NULL);
323 void avahi_cache_drop_record(AvahiCache *c, AvahiRecord *r) {
329 if ((e = avahi_cache_lookup_record(c, r)))
333 static void func(gpointer key, gpointer data, gpointer userdata) {
334 AvahiCacheEntry *e = data;
341 t = avahi_record_to_string(e->record);
342 fprintf((FILE*) userdata, "%s\n", t);
346 void avahi_cache_dump(AvahiCache *c, FILE *f) {
350 fprintf(f, ";;; CACHE DUMP FOLLOWS ;;;\n");
351 g_hash_table_foreach(c->hash_table, func, f);
354 gboolean avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e) {
361 g_get_current_time(&now);
363 age = avahi_timeval_diff(&now, &e->timestamp)/1000000;
365 g_message("age: %u, ttl/2: %u", age, e->record->ttl);
367 return age >= e->record->ttl/2;