diff options
author | Federico Lucifredi <flucifredi@acm.org> | 2006-12-19 19:11:45 +0000 |
---|---|---|
committer | Federico Lucifredi <flucifredi@acm.org> | 2006-12-19 19:11:45 +0000 |
commit | 6386804f93ee272fcff73459a9cfe0b847101609 (patch) | |
tree | 26fa54c9fdaeb608560ccaa249f153e04e6e5798 /trunk/avahi-core/probe-sched.c | |
parent | 16ed6d63c6c231cb5e606421ae3a245b2948185f (diff) |
Branching for wide-area publishing work
git-svn-id: file:///home/lennart/svn/public/avahi/branches/federico@1344 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
Diffstat (limited to 'trunk/avahi-core/probe-sched.c')
-rw-r--r-- | trunk/avahi-core/probe-sched.c | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/trunk/avahi-core/probe-sched.c b/trunk/avahi-core/probe-sched.c new file mode 100644 index 0000000..f430bce --- /dev/null +++ b/trunk/avahi-core/probe-sched.c @@ -0,0 +1,402 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> + +#include <avahi-common/domain.h> +#include <avahi-common/timeval.h> +#include <avahi-common/malloc.h> + +#include "probe-sched.h" +#include "log.h" +#include "rr-util.h" + +#define AVAHI_PROBE_HISTORY_MSEC 150 +#define AVAHI_PROBE_DEFER_MSEC 50 + +typedef struct AvahiProbeJob AvahiProbeJob; + +struct AvahiProbeJob { + AvahiProbeScheduler *scheduler; + AvahiTimeEvent *time_event; + + int chosen; /* Use for packet assembling */ + int done; + struct timeval delivery; + + AvahiRecord *record; + + AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs); +}; + +struct AvahiProbeScheduler { + AvahiInterface *interface; + AvahiTimeEventQueue *time_event_queue; + + AVAHI_LLIST_HEAD(AvahiProbeJob, jobs); + AVAHI_LLIST_HEAD(AvahiProbeJob, history); +}; + +static AvahiProbeJob* job_new(AvahiProbeScheduler *s, AvahiRecord *record, int done) { + AvahiProbeJob *pj; + + assert(s); + assert(record); + + if (!(pj = avahi_new(AvahiProbeJob, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; /* OOM */ + } + + pj->scheduler = s; + pj->record = avahi_record_ref(record); + pj->time_event = NULL; + pj->chosen = 0; + + if ((pj->done = done)) + AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj); + else + AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->jobs, pj); + + return pj; +} + +static void job_free(AvahiProbeScheduler *s, AvahiProbeJob *pj) { + assert(pj); + + if (pj->time_event) + avahi_time_event_free(pj->time_event); + + if (pj->done) + AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->history, pj); + else + AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj); + + avahi_record_unref(pj->record); + avahi_free(pj); +} + +static void elapse_callback(AvahiTimeEvent *e, void* data); + +static void job_set_elapse_time(AvahiProbeScheduler *s, AvahiProbeJob *pj, unsigned msec, unsigned jitter) { + struct timeval tv; + + assert(s); + assert(pj); + + avahi_elapse_time(&tv, msec, jitter); + + if (pj->time_event) + avahi_time_event_update(pj->time_event, &tv); + else + pj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, pj); +} + +static void job_mark_done(AvahiProbeScheduler *s, AvahiProbeJob *pj) { + assert(s); + assert(pj); + + assert(!pj->done); + + AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj); + AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj); + + pj->done = 1; + + job_set_elapse_time(s, pj, AVAHI_PROBE_HISTORY_MSEC, 0); + gettimeofday(&pj->delivery, NULL); +} + +AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i) { + AvahiProbeScheduler *s; + + assert(i); + + if (!(s = avahi_new(AvahiProbeScheduler, 1))) { + avahi_log_error(__FILE__": Out of memory"); + return NULL; + } + + s->interface = i; + s->time_event_queue = i->monitor->server->time_event_queue; + + AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->jobs); + AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->history); + + return s; +} + +void avahi_probe_scheduler_free(AvahiProbeScheduler *s) { + assert(s); + + avahi_probe_scheduler_clear(s); + avahi_free(s); +} + +void avahi_probe_scheduler_clear(AvahiProbeScheduler *s) { + assert(s); + + while (s->jobs) + job_free(s, s->jobs); + while (s->history) + job_free(s, s->history); +} + +static int packet_add_probe_query(AvahiProbeScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) { + size_t size; + AvahiKey *k; + int b; + + assert(s); + assert(p); + assert(pj); + + assert(!pj->chosen); + + /* Estimate the size for this record */ + size = + avahi_key_get_estimate_size(pj->record->key) + + avahi_record_get_estimate_size(pj->record); + + /* Too large */ + if (size > avahi_dns_packet_space(p)) + return 0; + + /* Create the probe query */ + if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) + return 0; /* OOM */ + + b = !!avahi_dns_packet_append_key(p, k, 0); + assert(b); + + /* Mark this job for addition to the packet */ + pj->chosen = 1; + + /* Scan for more jobs whith matching key pattern */ + for (pj = s->jobs; pj; pj = pj->jobs_next) { + if (pj->chosen) + continue; + + /* Does the record match the probe? */ + if (k->clazz != pj->record->key->clazz || !avahi_domain_equal(k->name, pj->record->key->name)) + continue; + + /* This job wouldn't fit in */ + if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_space(p)) + break; + + /* Mark this job for addition to the packet */ + pj->chosen = 1; + } + + avahi_key_unref(k); + + return 1; +} + +static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) { + AvahiProbeJob *pj = data, *next; + AvahiProbeScheduler *s; + AvahiDnsPacket *p; + unsigned n; + + assert(pj); + s = pj->scheduler; + + if (pj->done) { + /* Lets remove it from the history */ + job_free(s, pj); + return; + } + + if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu))) + return; /* OOM */ + n = 1; + + /* Add the import probe */ + if (!packet_add_probe_query(s, p, pj)) { + size_t size; + AvahiKey *k; + int b; + + avahi_dns_packet_free(p); + + /* The probe didn't fit in the package, so let's allocate a larger one */ + + size = + avahi_key_get_estimate_size(pj->record->key) + + avahi_record_get_estimate_size(pj->record) + + AVAHI_DNS_PACKET_HEADER_SIZE; + + if (size > AVAHI_DNS_PACKET_SIZE_MAX) + size = AVAHI_DNS_PACKET_SIZE_MAX; + + if (!(p = avahi_dns_packet_new_query(size))) + return; /* OOM */ + + if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) { + avahi_dns_packet_free(p); + return; /* OOM */ + } + + b = avahi_dns_packet_append_key(p, k, 0) && avahi_dns_packet_append_record(p, pj->record, 0, 0); + avahi_key_unref(k); + + if (b) { + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, 1); + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, 1); + avahi_interface_send_packet(s->interface, p); + } else + avahi_log_warn("Probe record too large, cannot send"); + + avahi_dns_packet_free(p); + job_mark_done(s, pj); + + return; + } + + /* Try to fill up packet with more probes, if available */ + for (pj = s->jobs; pj; pj = pj->jobs_next) { + + if (pj->chosen) + continue; + + if (!packet_add_probe_query(s, p, pj)) + break; + + n++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n); + + n = 0; + + /* Now add the chosen records to the authorative section */ + for (pj = s->jobs; pj; pj = next) { + + next = pj->jobs_next; + + if (!pj->chosen) + continue; + + if (!avahi_dns_packet_append_record(p, pj->record, 0, 0)) { +/* avahi_log_warn("Bad probe size estimate!"); */ + + /* Unmark all following jobs */ + for (; pj; pj = pj->jobs_next) + pj->chosen = 0; + + break; + } + + job_mark_done(s, pj); + + n ++; + } + + avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n); + + /* Send it now */ + avahi_interface_send_packet(s->interface, p); + avahi_dns_packet_free(p); +} + +static AvahiProbeJob* find_scheduled_job(AvahiProbeScheduler *s, AvahiRecord *record) { + AvahiProbeJob *pj; + + assert(s); + assert(record); + + for (pj = s->jobs; pj; pj = pj->jobs_next) { + assert(!pj->done); + + if (avahi_record_equal_no_ttl(pj->record, record)) + return pj; + } + + return NULL; +} + +static AvahiProbeJob* find_history_job(AvahiProbeScheduler *s, AvahiRecord *record) { + AvahiProbeJob *pj; + + assert(s); + assert(record); + + for (pj = s->history; pj; pj = pj->jobs_next) { + assert(pj->done); + + if (avahi_record_equal_no_ttl(pj->record, record)) { + /* Check whether this entry is outdated */ + + if (avahi_age(&pj->delivery) > AVAHI_PROBE_HISTORY_MSEC*1000) { + /* it is outdated, so let's remove it */ + job_free(s, pj); + return NULL; + } + + return pj; + } + } + + return NULL; +} + +int avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, int immediately) { + AvahiProbeJob *pj; + struct timeval tv; + + assert(s); + assert(record); + assert(!avahi_key_is_pattern(record->key)); + + if ((pj = find_history_job(s, record))) + return 0; + + avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0); + + if ((pj = find_scheduled_job(s, record))) { + + if (avahi_timeval_compare(&tv, &pj->delivery) < 0) { + /* If the new entry should be scheduled earlier, update the old entry */ + pj->delivery = tv; + avahi_time_event_update(pj->time_event, &pj->delivery); + } + + return 1; + } else { + /* Create a new job and schedule it */ + if (!(pj = job_new(s, record, 0))) + return 0; /* OOM */ + + pj->delivery = tv; + pj->time_event = avahi_time_event_new(s->time_event_queue, &pj->delivery, elapse_callback, pj); + + +/* avahi_log_debug("Accepted new probe job."); */ + + return 1; + } +} |