Commit 1f6bad74 authored by Jan Včelák's avatar Jan Včelák 🚀

events serialization: zone loading (without journal and signing)

parent fa84b4c3
......@@ -149,20 +149,24 @@ src/knot/updates/ddns.c
src/knot/updates/ddns.h
src/knot/updates/xfr-in.c
src/knot/updates/xfr-in.h
src/knot/worker/pool.c
src/knot/worker/pool.h
src/knot/worker/queue.c
src/knot/worker/queue.h
src/knot/worker/task.c
src/knot/worker/task.h
src/knot/zone/contents.c
src/knot/zone/contents.h
src/knot/zone/estimator.c
src/knot/zone/estimator.h
src/knot/zone/events.c
src/knot/zone/events.h
src/knot/zone/load.c
src/knot/zone/load.h
src/knot/zone/node.c
src/knot/zone/node.h
src/knot/zone/semantic-check.c
src/knot/zone/semantic-check.h
src/knot/zone/zone-contents.c
src/knot/zone/zone-contents.h
src/knot/zone/zone-create.c
src/knot/zone/zone-create.h
src/knot/zone/zone-diff.c
src/knot/zone/zone-diff.h
src/knot/zone/zone-dump.c
......@@ -173,6 +177,8 @@ src/knot/zone/zone.c
src/knot/zone/zone.h
src/knot/zone/zonedb.c
src/knot/zone/zonedb.h
src/knot/zone/zonefile.c
src/knot/zone/zonefile.h
src/libknot/binary.c
src/libknot/binary.h
src/libknot/common.h
......
......@@ -264,6 +264,8 @@ libknotd_la_SOURCES = \
knot/zone/estimator.h \
knot/zone/events.c \
knot/zone/events.h \
knot/zone/load.c \
knot/zone/load.h \
knot/zone/node.c \
knot/zone/node.h \
knot/zone/semantic-check.c \
......
......@@ -86,56 +86,6 @@ zone_t *load_zone_file(conf_zone_t *conf)
return NULL;
}
#if 0
zone_t *load_zone_file(conf_zone_t *conf)
{
assert(conf);
/* Open zone file for parsing. */
zloader_t zl;
int ret = zonefile_open(&zl, conf);
if (ret != KNOT_EOK) {
log_zone_error("Failed to open zone file '%s': %s\n",
conf->file, knot_strerror(ret));
return NULL;
}
struct stat st;
if (stat(conf->file, &st) < 0) {
/* Go silently and reset mtime to 0. */
memset(&st, 0, sizeof(struct stat));
}
/* Load the zone contents. */
knot_zone_contents_t *zone_contents = zonefile_load(&zl);
zonefile_close(&zl);
/* Check the loader result. */
if (zone_contents == NULL) {
log_zone_error("Failed to load zone file '%s'.\n", conf->file);
return NULL;
}
/* Create the new zone. */
zone_t *zone = zone_new((conf_zone_t *)conf);
if (zone == NULL) {
log_zone_error("Failed to create zone '%s': %s\n",
conf->name, knot_strerror(KNOT_ENOMEM));
knot_zone_contents_deep_free(&zone_contents);
return NULL;
}
/* Link zone contents to zone. */
zone->contents = zone_contents;
/* Save the timestamp from the zone db file. */
zone->zonefile_mtime = st.st_mtime;
zone->zonefile_serial = knot_zone_serial(zone->contents);
return zone;
}
#endif
/*!
* \brief Log message about loaded zone (name and status).
*
......@@ -166,17 +116,16 @@ static void log_zone_load_info(const zone_t *zone, const char *zone_name,
/*!
* \brief Load or reload the zone.
*
* \param conf Zone configuration.
* \param scheduler Server scheduler.
* \param old_zone Already loaded zone (can be NULL).
* \param conf Zone configuration.
* \param server Server.
* \param old_zone Already loaded zone (can be NULL).
*
* \return Error code, KNOT_EOK if successful.
*/
static zone_t *create_zone(conf_zone_t *conf, evsched_t *scheduler,
zone_t *old_zone)
static zone_t *create_zone(conf_zone_t *conf, server_t *server, zone_t *old_zone)
{
assert(conf);
assert(scheduler);
assert(server);
zone_t *zone = zone_new(conf);
if (!zone) {
......@@ -192,10 +141,11 @@ static zone_t *create_zone(conf_zone_t *conf, evsched_t *scheduler,
switch (zstatus) {
case ZONE_STATUS_FOUND_NEW:
case ZONE_STATUS_FOUND_UPDATED:
// zone_queue_enqueue(zone->events, ZONE_EVENT_RELOAD);
zone_events_schedule(zone, ZONE_EVENT_RELOAD, 1);
break;
case ZONE_STATUS_BOOSTRAP:
// will be triggered by timer
zone_events_schedule(zone, ZONE_EVENT_REFRESH, 1);
break;
case ZONE_STATUS_NOT_FOUND:
case ZONE_STATUS_FOUND_CURRENT:
break;
......@@ -203,10 +153,12 @@ static zone_t *create_zone(conf_zone_t *conf, evsched_t *scheduler,
assert(0);
}
/* Initialize zone timers. */
// TODO:
// zone_timers_create(zone, scheduler);
int result = zone_events_setup(zone, server->workers, &server->sched);
if (result != KNOT_EOK) {
zone->conf = NULL;
zone_free(&zone);
return NULL;
}
log_zone_load_info(zone, conf->name, zstatus);
......@@ -367,8 +319,6 @@ static knot_zonedb_t *create_zonedb(const conf_t *conf, server_t *server)
assert(server);
knot_zonedb_t *db_old = server->zone_db;
evsched_t *scheduler = &server->sched;
knot_zonedb_t *db_new = knot_zonedb_new(conf->zones_count);
if (!db_new) {
return NULL;
......@@ -382,10 +332,11 @@ static knot_zonedb_t *create_zonedb(const conf_t *conf, server_t *server)
zone_t *old_zone = knot_zonedb_find(db_old, apex);
knot_dname_free(&apex);
zone_t *zone = create_zone(zone_config, scheduler, old_zone);
zone_t *zone = create_zone(zone_config, server, old_zone);
if (!zone) {
log_server_error("Zone '%s' cannot be created.\n",
zone_config->name);
conf_free_zone(zone_config);
continue;
}
......@@ -446,7 +397,7 @@ int load_zones_from_config(const conf_t *conf, struct server_t *server)
/* Freeze zone timers. */
if (server->zone_db) {
// TODO: ne, tohle nechceme
knot_zonedb_foreach(server->zone_db, zone_events_freeze);
//knot_zonedb_foreach(server->zone_db, zone_events_freeze);
}
/* Insert all required zones to the new zone DB. */
......@@ -469,7 +420,7 @@ int load_zones_from_config(const conf_t *conf, struct server_t *server)
/* Thaw zone events now that the database is published. */
if (server->zone_db) {
knot_zonedb_foreach(server->zone_db, zone_events_thaw);
knot_zonedb_foreach(server->zone_db, zone_events_start);
// TODO: emit after loading
//knot_zonedb_foreach(server->zone_db, zones_schedule_notify, server);
}
......
......@@ -129,54 +129,6 @@ static uint32_t zones_soa_expire(zone_t *zone)
return zones_soa_timer(zone, knot_rdata_soa_expire);
}
/*!
* \brief XFR/IN expire event handler.
*/
int zones_expire_ev(event_t *event)
{
assert(event);
dbg_zones("zone: EXPIRE timer event\n");
if (event->data == NULL) {
return KNOT_EINVAL;
}
rcu_read_lock();
zone_t *zone = (zone_t *)event->data;
/* Check if zone is not discarded. */
if (zone->flags & ZONE_DISCARDED) {
rcu_read_unlock();
return KNOT_EOK;
}
zone_retain(zone); /* Keep a reference. */
rcu_read_unlock();
/* Early finish this event to prevent lockup during cancellation. */
evsched_end_process(event->sched);
/* Mark the zone as expired. This will remove the zone contents. */
knot_zone_contents_t *contents = zone_switch_contents(zone, NULL);
/* Publish expired zone, must be after evsched_event_finished.
* This is because some other thread may hold rcu_read_lock and
* wait for event cancellation. */
synchronize_rcu();
knot_zone_contents_deep_free(&contents);
/* Log event. */
log_zone_info("Zone '%s' expired.\n", zone->conf->name);
/* No more REFRESH/RETRY on expired zone. */
// evsched_cancel(zone->xfr_in.timer);
/* Release holding reference. */
zone_release(zone);
return KNOT_EOK;
}
/*!
* \brief Zone REFRESH or RETRY event.
*/
......@@ -198,7 +150,6 @@ int zones_refresh_ev(event_t *event)
}
/* Create XFR request. */
server_t *server = (server_t *)event->sched->ctx;
knot_ns_xfr_t *rq = xfr_task_create(zone, XFR_TYPE_SOA, XFR_FLAG_TCP);
rcu_read_unlock(); /* rq now holds a reference to zone */
if (!rq) {
......@@ -230,11 +181,6 @@ int zones_refresh_ev(event_t *event)
/* Issue request. */
#warning "XFR enqueue."
// ret = xfr_enqueue(server->xfr, rq);
// if (ret != KNOT_EOK) {
// xfr_task_free(rq);
// zone->xfr_in.state = XFR_SCHED; /* Revert state. */
// }
pthread_mutex_unlock(&zone->lock);
return ret;
}
......@@ -248,10 +194,6 @@ int zones_refresh_ev(event_t *event)
/* Issue request. */
evsched_end_process(event->sched);
#warning "XFR enqueue."
// ret = xfr_enqueue(server->xfr, rq);
// if (ret != KNOT_EOK) {
// xfr_task_free(rq);
// }
return ret;
}
......@@ -1874,7 +1816,7 @@ int zones_journal_apply(zone_t *zone)
}
rcu_read_lock();
knot_zone_contents_t *contents = zone->contents;
zone_contents_t *contents = zone->contents;
if (!contents) {
rcu_read_unlock();
return KNOT_ENOENT;
......@@ -1908,7 +1850,7 @@ int zones_journal_apply(zone_t *zone)
log_zone_info("Applying '%zu' changesets from journal "
"to zone '%s'.\n",
chsets->count, zone->conf->name);
knot_zone_contents_t *contents = NULL;
zone_contents_t *contents = NULL;
/*! \todo Journal changes may be applied directly,
* without need to copy the zone!!!
*/
......
......@@ -23,6 +23,9 @@ typedef struct task task_t;
typedef void (*task_cb)(task_t *);
/*!
* \brief Task executable by a worker.
*/
struct task {
void *ctx;
task_cb run;
......
......@@ -19,51 +19,150 @@
#include "common/evsched.h"
#include "knot/server/server.h"
#include "knot/updates/changesets.h"
#include "knot/worker/pool.h"
#include "knot/worker/task.h"
#include "knot/zone/events.h"
#include "knot/zone/load.h"
#include "knot/zone/zone.h"
/* -- zone events handling callbacks --------------------------------------- */
typedef void (*zone_event_cb)(zone_t *zone);
typedef int (*zone_event_cb)(zone_t *zone);
#include <stdio.h>
static void event_reload(zone_t *zone)
static int event_reload(zone_t *zone)
{
assert(zone);
fprintf(stderr, "LOADING ZONE %p\n", zone);
zone_contents_t *content = zone_load_contents(zone->conf);
if (!content) {
return KNOT_ERROR; // TODO: specific error code
}
int result = apply_journal(content, zone->ixfr_db);
if (result != KNOT_EOK) {
zone_contents_free(&content);
return result;
}
// TODO: do diff and sign
zone_contents_t *old = zone_switch_contents(zone, content);
if (old != NULL) {
synchronize_rcu();
zone_contents_deep_free(&old);
}
log_zone_info("Zone '%s' loaded.\n", zone->conf->name);
return KNOT_EOK;
}
static void event_refresh(zone_t *zone)
static int event_refresh(zone_t *zone)
{
assert(zone);
fprintf(stderr, "REFRESHING ZONE %p\n", zone);
// zone_schedule_event(zone, ZONE_EVENT_REFRESH, time);
return KNOT_ERROR;
}
static void event_expire(zone_t *zone)
static int event_expire(zone_t *zone)
{
assert(zone);
fprintf(stderr, "EXPIRING ZONE %p\n", zone);
zone_contents_t *expired = zone_switch_contents(zone, NULL);
synchronize_rcu();
zone_contents_deep_free(&expired);
log_zone_info("Zone '%s' expired.\n", zone->conf->name);
return KNOT_EOK;
}
static int event_flush(zone_t *zone)
{
return KNOT_ERROR;
}
static void event_dnssec(zone_t *zone)
static int event_dnssec(zone_t *zone)
{
assert(zone);
return KNOT_ERROR;
#if 0
knot_changesets_t *chs = knot_changesets_create();
if (chs == NULL) {
return KNOT_ENOMEM;
}
knot_changeset_t *ch = knot_changesets_create_changeset(chs);
if (ch == NULL) {
return KNOT_ENOMEM;
}
char *zname = knot_dname_to_str(zone->name);
msgpref = sprintf_alloc("DNSSEC: Zone %s -", zname);
free(zname);
if (msgpref == NULL) {
ret = KNOT_ENOMEM;
goto done;
}
if (force) {
log_zone_info("%s Complete resign started (dropping all "
"previous signatures)...\n", msgpref);
} else {
log_zone_info("%s Signing zone...\n", msgpref);
}
uint32_t new_serial = zones_next_serial(zone);
if (force) {
ret = knot_dnssec_zone_sign_force(zone->contents, zone->conf,
ch, refresh_at, new_serial);
} else {
ret = knot_dnssec_zone_sign(zone->contents, zone->conf,
ch, KNOT_SOA_SERIAL_UPDATE,
refresh_at, new_serial);
}
if (ret != KNOT_EOK) {
goto done;
}
if (!zones_changesets_empty(chs)) {
zone_contents_t *new_c = NULL;
ret = zones_store_and_apply_chgsets(chs, zone, &new_c, "DNSSEC",
XFR_TYPE_UPDATE);
chs = NULL; // freed by zones_store_and_apply_chgsets()
if (ret != KNOT_EOK) {
log_zone_error("%s Could not sign zone (%s).\n",
msgpref, knot_strerror(ret));
goto done;
}
}
log_zone_info("%s Successfully signed.\n", msgpref);
done:
knot_changesets_free(&chs);
free(msgpref);
return ret;
knot_
zones_dnssec_
fprintf(stderr, "RESIGNING ZONE %p\n", zone);
// zone_schedule_event(zone, ZONE_EVENT_REFRESH, time);
#endif
}
/* -- internal API --------------------------------------------------------- */
static bool valid_event(zone_event_type_t type)
{
return (type > 0 && type < ZONE_EVENT_COUNT);
return (type >= 0 && type < ZONE_EVENT_COUNT);
}
/*!
......@@ -111,17 +210,20 @@ static void event_set_time(zone_events_t *events, zone_event_type_t type, time_t
/*!
* \brief Cancel scheduled item, schedule first enqueued item.
*
* Make sure this is not called multiple times simultaneously.
* The events mutex must be locked when calling this function.
*/
static void reschedule(zone_events_t *events)
{
assert(events);
assert(events->event);
assert(pthread_mutex_trylock(&events->mx) == EBUSY);
evsched_cancel(events->event);
if (!events->event || events->running) {
return;
}
zone_event_type_t type = get_next_event(events);
if (!valid_event) {
evsched_cancel(events->event);
return;
}
......@@ -134,22 +236,36 @@ static void reschedule(zone_events_t *events)
/* -- callbacks control ---------------------------------------------------- */
/*!
* \brief Get callback for given type of event.
*/
static zone_event_cb get_event_callback(zone_event_type_t type)
typedef struct event_info_t {
zone_event_type_t type;
const zone_event_cb callback;
const char *name;
} event_info_t;
static const event_info_t EVENT_INFO[] = {
{ ZONE_EVENT_RELOAD, event_reload, "reload" },
{ ZONE_EVENT_REFRESH, event_refresh, "refresh" },
{ ZONE_EVENT_EXPIRE, event_expire, "expiration" },
{ ZONE_EVENT_FLUSH, event_flush, "journal flush" },
{ ZONE_EVENT_DNSSEC, event_dnssec, "DNSSEC resign" },
{ 0 }
};
static const event_info_t *get_event_info(zone_event_type_t type)
{
switch (type) {
case ZONE_EVENT_RELOAD: return event_reload;
case ZONE_EVENT_REFRESH: return event_refresh;
case ZONE_EVENT_EXPIRE: return event_expire;
case ZONE_EVENT_DNSSEC: return event_dnssec;
default: return NULL;
const event_info_t *info;
for (info = EVENT_INFO; info->callback != NULL; info++) {
if (info->type == type) {
return info;
}
}
assert(0);
return NULL;
}
/*!
* \brief Zone event wrapper, expected to be called from worker thread.
* \brief Zone event wrapper, expected to be called from a worker thread.
*
* 1. Takes the next planned event.
* 2. Resets the event's scheduled time.
......@@ -164,17 +280,27 @@ static void event_wrap(task_t *task)
zone_t *zone = task->ctx;
zone_events_t *events = &zone->events;
pthread_mutex_lock(&events->mx);
zone_event_type_t type = get_next_event(events);
if (!valid_event(type)) {
events->running = false;
pthread_mutex_unlock(&events->mx);
return;
}
event_set_time(events, type, 0);
pthread_mutex_unlock(&events->mx);
zone_event_cb run = get_event_callback(type);
assert(run);
const event_info_t *info = get_event_info(type);
int result = info->callback(zone);
if (result != KNOT_EOK) {
log_zone_error("[%s] %s failed - %s\n", zone->conf->name,
info->name, knot_strerror(result));
}
event_set_time(events, type, 0);
run(zone);
pthread_mutex_lock(&events->mx);
events->running = false;
reschedule(events);
pthread_mutex_unlock(&events->mx);
}
/*!
......@@ -186,31 +312,49 @@ static int event_dispatch(event_t *event)
assert(event->data);
zone_events_t *events = event->data;
worker_pool_assign(events->pool, &events->task);
pthread_mutex_lock(&events->mx);
if (!events->running) {
events->running = true;
worker_pool_assign(events->pool, &events->task);
}
pthread_mutex_unlock(&events->mx);
return KNOT_EOK;
}
/* -- public API ----------------------------------------------------------- */
int zone_events_init(zone_t *zone, server_t *server)
int zone_events_init(zone_t *zone)
{
if (!zone) {
return KNOT_EINVAL;
}
zone_events_t *events = &zone->events;
memset(&zone->events, 0, sizeof(zone->events));
pthread_mutex_init(&events->mx, NULL);
events->task.ctx = zone;
events->task.run = event_wrap;
return KNOT_EOK;
}
int zone_events_setup(zone_t *zone, worker_pool_t *workers, evsched_t *scheduler)
{
if (!zone || !server) {
if (!zone || !workers || !scheduler) {
return KNOT_EINVAL;
}
event_t *event = evsched_event_create(&server->sched, event_dispatch,
&zone->events);
event_t *event;
event = evsched_event_create(scheduler, event_dispatch, &zone->events);
if (!event) {
return KNOT_ENOMEM;
}
memset(&zone->events, 0, sizeof(zone->events));
zone->events.event = event;
zone->events.pool = server->workers;
zone->events.task.ctx = zone;
zone->events.task.run = event_wrap;
zone->events.pool = workers;
return KNOT_EOK;
}
......@@ -224,6 +368,9 @@ void zone_events_deinit(zone_t *zone)
evsched_cancel(zone->events.event);
evsched_event_free(zone->events.event);
assert(zone->events.running == false);
pthread_mutex_destroy(&zone->events.mx);
memset(&zone->events, 0, sizeof(zone->events));
}
......@@ -233,14 +380,17 @@ void zone_events_schedule(zone_t *zone, zone_event_type_t type, time_t time)
return;
}
event_set_time(&zone->events, type, time);
zone_events_t *events = &zone->events;
pthread_mutex_lock(&events->mx);
event_set_time(events, type, time);
reschedule(events);
pthread_mutex_unlock(&events->mx);
}
void zone_events_cancel(zone_t *zone, zone_event_type_t type)
{
zone_events_schedule(zone, type, 0);
reschedule(&zone->events);
}
void zone_events_cancel_all(zone_t *zone)
......@@ -249,9 +399,23 @@ void zone_events_cancel_all(zone_t *zone)
return;
}
zone_events_t *events = &zone->events;
pthread_mutex_lock(&events->mx);
for (int i = 0; i < ZONE_EVENT_COUNT; i++) {
event_set_time(&zone->events, i, 0);
event_set_time(events, i, 0);
}
reschedule(events);
pthread_mutex_unlock(&events->mx);
}
void zone_events_start(zone_t *zone)
{
if (!zone) {
return;
}
pthread_mutex_lock(&zone->events.mx);
reschedule(&zone->events);
pthread_mutex_unlock(&zone->events.mx);
}
......@@ -33,12 +33,16 @@ typedef enum zone_event_type {
ZONE_EVENT_RELOAD = 0,
ZONE_EVENT_REFRESH,
ZONE_EVENT_EXPIRE,
ZONE_EVENT_FLUSH,
ZONE_EVENT_DNSSEC,
// terminator
ZONE_EVENT_COUNT,
} zone_event_type_t;
typedef struct zone_events {
pthread_mutex_t mx; //!< Mutex protecting the struct.
bool running; //!< Some zone event is being run.
event_t *event; //!< Scheduler event.
worker_pool_t *pool; //!< Server worker pool.
......@@ -49,10 +53,21 @@ typedef struct zone_events {
/*!
* \brief Initialize zone events.
*
* \param events Zone events.
* \param zone Pointer to zone (context of execution).
* The function will not set up the scheduling, use \ref zone_events_enable
* to do that.
*
* \param zone Pointer to zone (context of execution).
*/
int zone_events_init(struct zone_t *zone, struct server_t *server);
int zone_events_init(struct zone_t *zone);
/*!
* \brief Set up zone events execution.
*
* \param workers Worker thread pool.
* \param scheduler Event scheduler.
*/
int zone_events_setup(struct zone_t *zone, worker_pool_t *workers,
evsched_t *scheduler);
/*!
* \brief Deinitialize zone events.
......@@ -73,3 +88,8 @@ void zone_events_cancel(struct zone_t *zone, zone_event_type_t type);
* \brief Cancel all zone events.
*/
void zone_events_cancel_all(struct zone_t *zone);
/*!
* \brief Start the events processing.
*/
void zone_events_start(struct zone_t *zone);
/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/log.h"
#include "knot/server/journal.h"
#include "knot/zone/contents.h"
#include "knot/zone/load.h"
#include "knot/zone/zone.h"
#include "knot/zone/zonefile.h"
#include "libknot/rdata.h"