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