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 <stdlib.h> 27 28#include <avahi-common/timeval.h> 29#include <avahi-common/malloc.h> 30#include <avahi-common/error.h> 31#include <avahi-common/domain.h> 32 33#include "querier.h" 34#include "log.h" 35 36struct AvahiQuerier { 37 AvahiInterface *interface; 38 39 AvahiKey *key; 40 int n_used; 41 42 unsigned sec_delay; 43 44 AvahiTimeEvent *time_event; 45 46 struct timeval creation_time; 47 48 unsigned post_id; 49 int post_id_valid; 50 51 AVAHI_LLIST_FIELDS(AvahiQuerier, queriers); 52}; 53 54void avahi_querier_free(AvahiQuerier *q) { 55 assert(q); 56 57 AVAHI_LLIST_REMOVE(AvahiQuerier, queriers, q->interface->queriers, q); 58 avahi_hashmap_remove(q->interface->queriers_by_key, q->key); 59 60 avahi_key_unref(q->key); 61 avahi_time_event_free(q->time_event); 62 63 avahi_free(q); 64} 65 66static void querier_elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) { 67 AvahiQuerier *q = userdata; 68 struct timeval tv; 69 70 assert(q); 71 72 if (q->n_used <= 0) { 73 74 /* We are not referenced by anyone anymore, so let's free 75 * ourselves. We should not send out any further queries from 76 * this querier object anymore. */ 77 78 avahi_querier_free(q); 79 return; 80 } 81 82 if (avahi_interface_post_query(q->interface, q->key, 0, &q->post_id)) { 83 84 /* The queue accepted our query. We store the query id here, 85 * that allows us to drop the query at a later point if the 86 * query is very short-lived. */ 87 88 q->post_id_valid = 1; 89 } 90 91 q->sec_delay *= 2; 92 93 if (q->sec_delay >= 60*60) /* 1h */ 94 q->sec_delay = 60*60; 95 96 avahi_elapse_time(&tv, q->sec_delay*1000, 0); 97 avahi_time_event_update(q->time_event, &tv); 98} 99 100void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_ctime) { 101 AvahiQuerier *q; 102 struct timeval tv; 103 104 assert(i); 105 assert(key); 106 107 if ((q = avahi_hashmap_lookup(i->queriers_by_key, key))) { 108 109 /* Someone is already browsing for records of this RR key */ 110 q->n_used++; 111 112 /* Return the creation time. This is used for generating the 113 * ALL_FOR_NOW event one second after the querier was 114 * initially created. */ 115 if (ret_ctime) 116 *ret_ctime = q->creation_time; 117 return; 118 } 119 120 /* No one is browsing for this RR key, so we add a new querier */ 121 if (!(q = avahi_new(AvahiQuerier, 1))) 122 return; /* OOM */ 123 124 q->key = avahi_key_ref(key); 125 q->interface = i; 126 q->n_used = 1; 127 q->sec_delay = 1; 128 q->post_id_valid = 0; 129 gettimeofday(&q->creation_time, NULL); 130 131 /* Do the initial query */ 132 if (avahi_interface_post_query(i, key, 0, &q->post_id)) 133 q->post_id_valid = 1; 134 135 /* Schedule next queries */ 136 q->time_event = avahi_time_event_new(i->monitor->server->time_event_queue, avahi_elapse_time(&tv, q->sec_delay*1000, 0), querier_elapse_callback, q); 137 138 AVAHI_LLIST_PREPEND(AvahiQuerier, queriers, i->queriers, q); 139 avahi_hashmap_insert(i->queriers_by_key, q->key, q); 140 141 /* Return the creation time. This is used for generating the 142 * ALL_FOR_NOW event one second after the querier was initially 143 * created. */ 144 if (ret_ctime) 145 *ret_ctime = q->creation_time; 146} 147 148void avahi_querier_remove(AvahiInterface *i, AvahiKey *key) { 149 AvahiQuerier *q; 150 151 if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key)) || q->n_used <= 0) { 152 /* There was no querier for this RR key, or it wasn't referenced by anyone */ 153 avahi_log_warn(__FILE__": querier_remove() called but no querier to remove."); 154 return; 155 } 156 157 if ((--q->n_used) <= 0) { 158 159 /* Nobody references us anymore. */ 160 161 if (q->post_id_valid && avahi_interface_withraw_query(i, q->post_id)) { 162 163 /* We succeeded in withdrawing our query from the queue, 164 * so let's drop dead. */ 165 166 avahi_querier_free(q); 167 } 168 169 /* If we failed to withdraw our query from the queue, we stay 170 * alive, in case someone else might recycle our querier at a 171 * later point. We are freed at our next expiry, in case 172 * nobody recycled us. */ 173 } 174} 175 176static void remove_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { 177 assert(m); 178 assert(i); 179 assert(userdata); 180 181 if (i->announcing) 182 avahi_querier_remove(i, (AvahiKey*) userdata); 183} 184 185void avahi_querier_remove_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key) { 186 assert(s); 187 assert(key); 188 189 avahi_interface_monitor_walk(s->monitor, idx, protocol, remove_querier_callback, key); 190} 191 192struct cbdata { 193 AvahiKey *key; 194 struct timeval *ret_ctime; 195}; 196 197static void add_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) { 198 struct cbdata *cbdata = userdata; 199 200 assert(m); 201 assert(i); 202 assert(cbdata); 203 204 if (i->announcing) { 205 struct timeval tv; 206 avahi_querier_add(i, cbdata->key, &tv); 207 208 if (cbdata->ret_ctime && avahi_timeval_compare(&tv, cbdata->ret_ctime) > 0) 209 *cbdata->ret_ctime = tv; 210 } 211} 212 213void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, struct timeval *ret_ctime) { 214 struct cbdata cbdata; 215 216 assert(s); 217 assert(key); 218 219 cbdata.key = key; 220 cbdata.ret_ctime = ret_ctime; 221 222 if (ret_ctime) 223 ret_ctime->tv_sec = ret_ctime->tv_usec = 0; 224 225 avahi_interface_monitor_walk(s->monitor, idx, protocol, add_querier_callback, &cbdata); 226} 227 228int avahi_querier_shall_refresh_cache(AvahiInterface *i, AvahiKey *key) { 229 AvahiQuerier *q; 230 231 assert(i); 232 assert(key); 233 234 /* Called by the cache maintainer */ 235 236 if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key))) 237 /* This key is currently not subscribed at all, so no cache 238 * refresh is needed */ 239 return 0; 240 241 if (q->n_used <= 0) { 242 243 /* If this is an entry nobody references right now, don't 244 * consider it "existing". */ 245 246 /* Remove this querier since it is referenced by nobody 247 * and the cached data will soon be out of date */ 248 avahi_querier_free(q); 249 250 /* Tell the cache that no refresh is needed */ 251 return 0; 252 253 } else { 254 struct timeval tv; 255 256 /* We can defer our query a little, since the cache will now 257 * issue a refresh query anyway. */ 258 avahi_elapse_time(&tv, q->sec_delay*1000, 0); 259 avahi_time_event_update(q->time_event, &tv); 260 261 /* Tell the cache that a refresh should be issued */ 262 return 1; 263 } 264} 265 266void avahi_querier_free_all(AvahiInterface *i) { 267 assert(i); 268 269 while (i->queriers) 270 avahi_querier_free(i->queriers); 271} 272