"Fossies" - the Fresh Open Source Software Archive

Member "knot-2.9.2/src/knot/zone/zonedb-load.c" (12 Dec 2019, 9015 Bytes) of package /linux/misc/dns/knot-2.9.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "zonedb-load.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.9.1_vs_2.9.2.

    1 /*  Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
    2 
    3     This program is free software: you can redistribute it and/or modify
    4     it under the terms of the GNU General Public License as published by
    5     the Free Software Foundation, either version 3 of the License, or
    6     (at your option) any later version.
    7 
    8     This program is distributed in the hope that it will be useful,
    9     but WITHOUT ANY WARRANTY; without even the implied warranty of
   10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11     GNU General Public License for more details.
   12 
   13     You should have received a copy of the GNU General Public License
   14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
   15  */
   16 
   17 #include <assert.h>
   18 #include <urcu.h>
   19 
   20 #include "knot/common/log.h"
   21 #include "knot/conf/module.h"
   22 #include "knot/events/replan.h"
   23 #include "knot/zone/timers.h"
   24 #include "knot/zone/zone-load.h"
   25 #include "knot/zone/zone.h"
   26 #include "knot/zone/zonedb-load.h"
   27 #include "knot/zone/zonedb.h"
   28 #include "knot/zone/zonefile.h"
   29 #include "libknot/libknot.h"
   30 
   31 static bool zone_file_updated(conf_t *conf, const zone_t *old_zone,
   32                               const knot_dname_t *zone_name)
   33 {
   34     assert(conf);
   35     assert(zone_name);
   36 
   37     char *zonefile = conf_zonefile(conf, zone_name);
   38     struct timespec mtime;
   39     int ret = zonefile_exists(zonefile, &mtime);
   40     free(zonefile);
   41 
   42     return (ret == KNOT_EOK && old_zone != NULL &&
   43             !(old_zone->zonefile.exists &&
   44           old_zone->zonefile.mtime.tv_sec == mtime.tv_sec &&
   45           old_zone->zonefile.mtime.tv_nsec == mtime.tv_nsec));
   46 }
   47 
   48 static zone_t *create_zone_from(const knot_dname_t *name, server_t *server)
   49 {
   50     zone_t *zone = zone_new(name);
   51     if (!zone) {
   52         return NULL;
   53     }
   54 
   55     zone->journaldb = &server->journaldb;
   56     zone->kaspdb = &server->kaspdb;
   57 
   58     int result = zone_events_setup(zone, server->workers, &server->sched);
   59     if (result != KNOT_EOK) {
   60         zone_free(&zone);
   61         return NULL;
   62     }
   63 
   64     return zone;
   65 }
   66 
   67 /*!
   68  * \brief Set timer if unset (value is 0).
   69  */
   70 static void time_set_default(time_t *time, time_t value)
   71 {
   72     assert(time);
   73 
   74     if (*time == 0) {
   75         *time = value;
   76     }
   77 }
   78 
   79 /*!
   80  * \brief Set default timers for new zones or invalidate if not valid.
   81  */
   82 static void timers_sanitize(conf_t *conf, zone_t *zone)
   83 {
   84     assert(conf);
   85     assert(zone);
   86 
   87     time_t now = time(NULL);
   88 
   89     // replace SOA expire if we have better knowledge
   90     if (!zone_contents_is_empty(zone->contents)) {
   91         const knot_rdataset_t *soa = zone_soa(zone);
   92         zone->timers.soa_expire = knot_soa_expire(soa->rdata);
   93     }
   94 
   95     // assume now if we don't know when we flushed
   96     time_set_default(&zone->timers.last_flush, now);
   97 
   98     if (zone_is_slave(conf, zone)) {
   99         // assume now if we don't know
  100         time_set_default(&zone->timers.last_refresh, now);
  101         time_set_default(&zone->timers.next_refresh, now);
  102     } else {
  103         // invalidate if we don't have a master
  104         zone->timers.last_refresh = 0;
  105         zone->timers.next_refresh = 0;
  106     }
  107 }
  108 
  109 static zone_t *create_zone_reload(conf_t *conf, const knot_dname_t *name,
  110                                   server_t *server, zone_t *old_zone)
  111 {
  112     zone_t *zone = create_zone_from(name, server);
  113     if (!zone) {
  114         return NULL;
  115     }
  116 
  117     zone->contents = old_zone->contents;
  118 
  119     zone->timers = old_zone->timers;
  120     timers_sanitize(conf, zone);
  121 
  122     if (zone_file_updated(conf, old_zone, name) && !zone_expired(zone)) {
  123         replan_load_updated(zone, old_zone);
  124     } else {
  125         zone->zonefile = old_zone->zonefile;
  126         replan_load_current(conf, zone, old_zone);
  127     }
  128 
  129     if (old_zone->control_update != NULL) {
  130         log_zone_warning(old_zone->name, "control transaction aborted");
  131         zone_control_clear(old_zone);
  132     }
  133 
  134     return zone;
  135 }
  136 
  137 static zone_t *create_zone_new(conf_t *conf, const knot_dname_t *name,
  138                                server_t *server)
  139 {
  140     zone_t *zone = create_zone_from(name, server);
  141     if (!zone) {
  142         return NULL;
  143     }
  144 
  145     int ret = zone_timers_read(&server->timerdb, name, &zone->timers);
  146     if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
  147         log_zone_error(zone->name, "failed to load persistent timers (%s)",
  148                        knot_strerror(ret));
  149         zone_free(&zone);
  150         return NULL;
  151     }
  152 
  153     timers_sanitize(conf, zone);
  154 
  155     if (zone_expired(zone)) {
  156         // expired => force bootstrap, no load attempt
  157         log_zone_info(zone->name, "zone will be bootstrapped");
  158         assert(zone_is_slave(conf, zone));
  159         replan_load_bootstrap(conf, zone);
  160     } else {
  161         log_zone_info(zone->name, "zone will be loaded");
  162         replan_load_new(zone); // if load fails, fallback to bootstrap
  163     }
  164 
  165     return zone;
  166 }
  167 
  168 /*!
  169  * \brief Load or reload the zone.
  170  *
  171  * \param conf       Configuration.
  172  * \param server     Server.
  173  * \param old_zone   Already loaded zone (can be NULL).
  174  *
  175  * \return Error code, KNOT_EOK if successful.
  176  */
  177 static zone_t *create_zone(conf_t *conf, const knot_dname_t *name, server_t *server,
  178                            zone_t *old_zone)
  179 {
  180     assert(conf);
  181     assert(name);
  182     assert(server);
  183 
  184     if (old_zone) {
  185         return create_zone_reload(conf, name, server, old_zone);
  186     } else {
  187         return create_zone_new(conf, name, server);
  188     }
  189 }
  190 
  191 static void mark_changed_zones(knot_zonedb_t *zonedb, trie_t *changed)
  192 {
  193     if (changed == NULL) {
  194         return;
  195     }
  196 
  197     trie_it_t *it = trie_it_begin(changed);
  198     for (; !trie_it_finished(it); trie_it_next(it)) {
  199         const knot_dname_t *name =
  200             (const knot_dname_t *)trie_it_key(it, NULL);
  201 
  202         zone_t *zone = knot_zonedb_find(zonedb, name);
  203         if (zone != NULL) {
  204             conf_io_type_t type = (conf_io_type_t)(*trie_it_val(it));
  205             assert(!(type & CONF_IO_TSET));
  206             zone->change_type = type;
  207         }
  208     }
  209     trie_it_free(it);
  210 }
  211 
  212 /*!
  213  * \brief Create new zone database.
  214  *
  215  * Zones that should be retained are just added from the old database to the
  216  * new. New zones are loaded.
  217  *
  218  * \param conf    New server configuration.
  219  * \param server  Server instance.
  220  *
  221  * \return New zone database.
  222  */
  223 static knot_zonedb_t *create_zonedb(conf_t *conf, server_t *server)
  224 {
  225     assert(conf);
  226     assert(server);
  227 
  228     knot_zonedb_t *db_old = server->zone_db;
  229     knot_zonedb_t *db_new = knot_zonedb_new();
  230     if (!db_new) {
  231         return NULL;
  232     }
  233 
  234     bool full = !(conf->io.flags & CONF_IO_FACTIVE) ||
  235                 (conf->io.flags & CONF_IO_FRLD_ZONES);
  236 
  237     /* Mark changed zones. */
  238     if (!full) {
  239         mark_changed_zones(server->zone_db, conf->io.zones);
  240     }
  241 
  242     for (conf_iter_t iter = conf_iter(conf, C_ZONE); iter.code == KNOT_EOK;
  243          conf_iter_next(conf, &iter)) {
  244         conf_val_t id = conf_iter_id(conf, &iter);
  245         const knot_dname_t *name = conf_dname(&id);
  246 
  247         zone_t *old_zone = knot_zonedb_find(db_old, name);
  248         if (old_zone != NULL && !full) {
  249             /* Reuse unchanged zone. */
  250             if (!(old_zone->change_type & CONF_IO_TRELOAD)) {
  251                 knot_zonedb_insert(db_new, old_zone);
  252                 continue;
  253             }
  254         }
  255 
  256         zone_t *zone = create_zone(conf, name, server, old_zone);
  257         if (zone == NULL) {
  258             log_zone_error(name, "zone cannot be created");
  259             continue;
  260         }
  261 
  262         conf_activate_modules(conf, zone->name, &zone->query_modules,
  263                               &zone->query_plan);
  264 
  265         knot_zonedb_insert(db_new, zone);
  266     }
  267 
  268     return db_new;
  269 }
  270 
  271 /*!
  272  * \brief Schedule deletion of old zones, and free the zone db structure.
  273  *
  274  * \note Zone content may be preserved in the new zone database, in this case
  275  *       new and old zone share the contents. Shared content is not freed.
  276  *
  277  * \param conf    New server configuration.
  278  * \param db_old  Old zone database to remove.
  279  * \param db_new  New zone database for comparison if full reload.
  280   */
  281 static void remove_old_zonedb(conf_t *conf, knot_zonedb_t *db_old,
  282                               knot_zonedb_t *db_new)
  283 {
  284     if (db_old == NULL) {
  285         return;
  286     }
  287 
  288     bool full = !(conf->io.flags & CONF_IO_FACTIVE) ||
  289                 (conf->io.flags & CONF_IO_FRLD_ZONES);
  290 
  291     knot_zonedb_iter_t *it = knot_zonedb_iter_begin(db_old);
  292 
  293     while (!knot_zonedb_iter_finished(it)) {
  294         zone_t *zone = knot_zonedb_iter_val(it);
  295 
  296         if (full) {
  297             /* Check if reloaded (reused contents). */
  298             if (knot_zonedb_find(db_new, zone->name)) {
  299                 zone->contents = NULL;
  300             }
  301             /* Completely new zone. */
  302         } else {
  303             /* Check if reloaded (reused contents). */
  304             if (zone->change_type & CONF_IO_TRELOAD) {
  305                 zone->contents = NULL;
  306                 zone_free(&zone);
  307             /* Check if removed (drop also contents). */
  308             } else if (zone->change_type & CONF_IO_TUNSET) {
  309                 zone_free(&zone);
  310             }
  311             /* Completely reused zone. */
  312         }
  313 
  314         knot_zonedb_iter_next(it);
  315     }
  316 
  317     knot_zonedb_iter_free(it);
  318 
  319     if (full) {
  320         knot_zonedb_deep_free(&db_old, false);
  321     } else {
  322         knot_zonedb_free(&db_old);
  323     }
  324 }
  325 
  326 void zonedb_reload(conf_t *conf, server_t *server)
  327 {
  328     if (conf == NULL || server == NULL) {
  329         return;
  330     }
  331 
  332     /* Insert all required zones to the new zone DB. */
  333     knot_zonedb_t *db_new = create_zonedb(conf, server);
  334     if (db_new == NULL) {
  335         log_error("failed to create new zone database");
  336         return;
  337     }
  338 
  339     /* Switch the databases. */
  340     knot_zonedb_t **db_current = &server->zone_db;
  341     knot_zonedb_t *db_old = rcu_xchg_pointer(db_current, db_new);
  342 
  343     /* Wait for readers to finish reading old zone database. */
  344     synchronize_rcu();
  345 
  346     /* Remove old zone DB. */
  347     remove_old_zonedb(conf, db_old, db_new);
  348 }