external-validation: implemented configurable timeout

This commit is contained in:
Libor Peltan 2025-06-16 17:46:16 +02:00 committed by Daniel Salzman
parent 965f9e1ecf
commit 3900b8cbd7
7 changed files with 94 additions and 4 deletions

View file

@ -2559,6 +2559,7 @@ External zone validation configuration.
external:
- id: STR
timeout: TIME
dump-new-zone: STR
dump-removals: STR
dump-additions: STR
@ -2570,6 +2571,16 @@ id
An external section identifier.
.. _external_timeout:
timeout
-------
If the validation is not confirmed within this time interval in seconds,
it is considered failed.
*Default:* ``300``
.. _external_dump-new-zone:
dump-new-zone

View file

@ -6,8 +6,11 @@
#include "semaphore.h"
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#if defined(__APPLE__)
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@ -66,6 +69,52 @@ void knot_sem_wait(knot_sem_t *sem)
}
}
static void timespec_now_shift(struct timespec *ts, unsigned long shift_ms)
{
clock_gettime(CLOCK_REALTIME, ts);
uint64_t nsec = ts->tv_nsec + shift_ms * 1000000LU;
ts->tv_sec += nsec / 1000000000LU;
ts->tv_nsec = nsec % 1000000000LU;
}
static bool timespec_past(const struct timespec *ts)
{
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
return ts->tv_sec == now.tv_sec ? ts->tv_nsec <= now.tv_nsec : ts->tv_sec < now.tv_sec;
}
bool knot_sem_timedwait(knot_sem_t *sem, unsigned long ms)
{
assert(sem != NULL);
if (ms == 0) {
knot_sem_wait(sem);
return true;
}
struct timespec end;
timespec_now_shift(&end, ms);
if (sem->status == SEM_STATUS_POSIX) {
int semret;
do {
semret = sem_timedwait(&sem->semaphore, &end);
} while (semret != 0 && errno != ETIMEDOUT); // repeat wait as it might be interrupted by a signal
return (semret == 0);
} else {
pthread_mutex_lock(&sem->status_lock->mutex);
while (sem->status <= 0 && !timespec_past(&end)) {
pthread_cond_timedwait(&sem->status_lock->cond, &sem->status_lock->mutex, &end);
}
if (sem->status <= 0) {
pthread_mutex_unlock(&sem->status_lock->mutex);
return false;
}
sem->status--;
pthread_mutex_unlock(&sem->status_lock->mutex);
return true;
}
}
void knot_sem_wait_post(knot_sem_t *sem)
{
assert((sem != NULL) && (sem->status != SEM_STATUS_POSIX));

View file

@ -7,6 +7,7 @@
#include <pthread.h>
#include <semaphore.h>
#include <stdbool.h>
typedef struct {
pthread_mutex_t mutex;
@ -42,6 +43,16 @@ void knot_sem_reset(knot_sem_t *sem, int value);
*/
void knot_sem_wait(knot_sem_t *sem);
/*!
* \brief Lock the semaphore (decrement), block until it's non-negative but only for specified timeout.
*
* \param sem Semapthore.
* \param ms Timeout in milliseconds or 0 for infinity (same as knot_sem_wait).
*
* \return True if semaphore acquired, false if timeout passed.
*/
bool knot_sem_timedwait(knot_sem_t *sem, unsigned long ms);
/*!
* \brief Block until the semaphore could decrement, but keep the value unchanged.
* \note This can be only used with nonposix semaphore.

View file

@ -457,6 +457,7 @@ static const yp_item_t desc_policy[] = {
static const yp_item_t desc_external[] = {
{ C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF },
{ C_TIMEOUT, YP_TINT, YP_VINT = { 0, UINT32_MAX, 300, YP_STIME } },
{ C_DUMP_NEW, YP_TSTR, YP_VSTR = { "" } },
{ C_DUMP_REM, YP_TSTR, YP_VSTR = { "" } },
{ C_DUMP_ADD, YP_TSTR, YP_VSTR = { "" } },

View file

@ -1005,7 +1005,8 @@ int zone_update_external(conf_t *conf, zone_update_t *update, conf_val_t *ev_id)
dbus_emit_external_verify(update->zone->name);
}
knot_sem_wait(&update->external);
val = conf_id_get(conf, C_EXTERNAL, C_TIMEOUT, ev_id);
knot_sem_timedwait(&update->external, conf_int(&val) * 1000);
pthread_mutex_lock(&update->zone->cu_lock);
update->zone->control_update = NULL;

View file

@ -40,7 +40,10 @@ ZONE = zone[0].name
LOG = "for external validation"
slave.async_start = True
slave.zones[ZONE].external = { "new": dump_file(slave, "new"), "rem": dump_file(slave, "diff"), "add": dump_file(slave, "diff") }
slave.zones[ZONE].external = { "timeout": "10",
"new": dump_file(slave, "new"),
"rem": dump_file(slave, "diff"),
"add": dump_file(slave, "diff") }
def check_diff_types(types):
check_zf_types(slave.zones[ZONE].external["add"], types)
@ -83,20 +86,33 @@ resp = ctl.receive_block()
t.sleep(2)
resp = slave.dig(ZONE, "SOA")
resp.check_soa_serial(serial - 1)
ctl.close()
up = master.update(zone)
up.add("snail", 3600, "AAAA", "1::1")
up.send()
serial = master.zone_wait(zone, serial)
t.sleep(2)
log_count_expect(slave, LOG, 3)
t.sleep(int(slave.zones[ZONE].external["timeout"]))
resp = slave.dig(ZONE, "SOA")
resp.check_soa_serial(serial - 2)
up = master.update(zone)
up.add("shark", 3600, "AAAA", "1::1")
up.send()
serial = master.zone_wait(zone, serial)
ctl.connect(os.path.join(slave.dir, sockname))
t.sleep(2)
log_count_expect(slave, LOG, 3)
log_count_expect(slave, LOG, 4)
ctl.send_block(cmd="zone-diff", zone=ZONE)
resp = ctl.receive_block()
isset("AAAA" in resp[ZONE]["horse."+ZONE], "ZONE-DIFF 2")
isset("AAAA" in resp[ZONE]["shark."+ZONE], "ZONE-DIFF 3")
isset("AAAA" in resp[ZONE]["snail."+ZONE], "ZONE-DIFF 3.5")
isset("AAAA" in resp[ZONE]["tiger."+ZONE], "ZONE-DIFF 4")
check_diff_types(["SOA", "SOA", "AAAA", "AAAA", "AAAA"])
check_diff_types(["SOA", "SOA", "AAAA", "AAAA", "AAAA", "AAAA"])
ctl.send_block(cmd="zone-commit", zone=ZONE)
resp = ctl.receive_block()
t.sleep(2)

View file

@ -1799,6 +1799,7 @@ class Knot(Server):
s.begin("external")
have_external = True
s.id_item("id", z.name)
self._str(s, "timeout", z.external["timeout"])
self._str(s, "dump-new-zone", z.external["new"])
self._str(s, "dump-removals", z.external["rem"])
self._str(s, "dump-additions", z.external["add"])