mirror of
https://github.com/openbsd/src.git
synced 2024-12-22 07:27:59 -08:00
222e275fb8
OK tb@
631 lines
17 KiB
C
631 lines
17 KiB
C
/* $OpenBSD: rrdp_notification.c,v 1.21 2024/04/12 11:50:29 job Exp $ */
|
|
/*
|
|
* Copyright (c) 2020 Nils Fisher <nils_fisher@hotmail.com>
|
|
* Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <assert.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <expat.h>
|
|
#include <openssl/sha.h>
|
|
|
|
#include "extern.h"
|
|
#include "rrdp.h"
|
|
|
|
enum notification_scope {
|
|
NOTIFICATION_SCOPE_START,
|
|
NOTIFICATION_SCOPE_NOTIFICATION,
|
|
NOTIFICATION_SCOPE_SNAPSHOT,
|
|
NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT,
|
|
NOTIFICATION_SCOPE_DELTA,
|
|
NOTIFICATION_SCOPE_END
|
|
};
|
|
|
|
struct delta_item {
|
|
char *uri;
|
|
char hash[SHA256_DIGEST_LENGTH];
|
|
long long serial;
|
|
TAILQ_ENTRY(delta_item) q;
|
|
};
|
|
|
|
TAILQ_HEAD(delta_q, delta_item);
|
|
|
|
struct notification_xml {
|
|
XML_Parser parser;
|
|
struct rrdp_session *repository;
|
|
struct rrdp_session *current;
|
|
const char *notifyuri;
|
|
char *session_id;
|
|
char *snapshot_uri;
|
|
char snapshot_hash[SHA256_DIGEST_LENGTH];
|
|
struct delta_q delta_q;
|
|
long long serial;
|
|
long long min_serial;
|
|
int version;
|
|
enum notification_scope scope;
|
|
};
|
|
|
|
static void free_delta(struct delta_item *);
|
|
|
|
static int
|
|
add_delta(struct notification_xml *nxml, const char *uri,
|
|
const char hash[SHA256_DIGEST_LENGTH], long long serial)
|
|
{
|
|
struct delta_item *d, *n;
|
|
|
|
if ((d = calloc(1, sizeof(struct delta_item))) == NULL)
|
|
err(1, "%s - calloc", __func__);
|
|
|
|
d->serial = serial;
|
|
d->uri = xstrdup(uri);
|
|
memcpy(d->hash, hash, sizeof(d->hash));
|
|
|
|
/* optimise for a sorted input */
|
|
n = TAILQ_LAST(&nxml->delta_q, delta_q);
|
|
if (n == NULL)
|
|
TAILQ_INSERT_HEAD(&nxml->delta_q, d, q);
|
|
else if (n->serial < serial)
|
|
TAILQ_INSERT_TAIL(&nxml->delta_q, d, q);
|
|
else
|
|
TAILQ_FOREACH(n, &nxml->delta_q, q) {
|
|
if (n->serial == serial) {
|
|
warnx("duplicate delta serial %lld ", serial);
|
|
free_delta(d);
|
|
return 0;
|
|
}
|
|
if (n->serial > serial) {
|
|
TAILQ_INSERT_BEFORE(n, d, q);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* check that there are no holes in the list */
|
|
static int
|
|
check_delta(struct notification_xml *nxml)
|
|
{
|
|
struct delta_item *d;
|
|
long long serial = 0;
|
|
|
|
TAILQ_FOREACH(d, &nxml->delta_q, q) {
|
|
if (serial != 0 && serial + 1 != d->serial)
|
|
return 0;
|
|
serial = d->serial;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
free_delta(struct delta_item *d)
|
|
{
|
|
free(d->uri);
|
|
free(d);
|
|
}
|
|
|
|
/*
|
|
* Parse a delta serial and hash line at idx from the rrdp session state.
|
|
* Return the serial or 0 on error. If hash is non-NULL, it is set to the
|
|
* start of the hash string on success.
|
|
*/
|
|
static long long
|
|
delta_parse(struct rrdp_session *s, size_t idx, char **hash)
|
|
{
|
|
long long serial;
|
|
char *line, *ep;
|
|
|
|
if (hash != NULL)
|
|
*hash = NULL;
|
|
if (idx < 0 || idx >= sizeof(s->deltas) / sizeof(s->deltas[0]))
|
|
return 0;
|
|
if ((line = s->deltas[idx]) == NULL)
|
|
return 0;
|
|
|
|
errno = 0;
|
|
serial = strtoll(line, &ep, 10);
|
|
if (line[0] == '\0' || *ep != ' ')
|
|
return 0;
|
|
if (serial <= 0 || (errno == ERANGE && serial == LLONG_MAX))
|
|
return 0;
|
|
|
|
if (hash != NULL)
|
|
*hash = ep + 1;
|
|
return serial;
|
|
}
|
|
|
|
static void
|
|
start_notification_elem(struct notification_xml *nxml, const char **attr)
|
|
{
|
|
XML_Parser p = nxml->parser;
|
|
int has_xmlns = 0;
|
|
size_t i;
|
|
|
|
if (nxml->scope != NOTIFICATION_SCOPE_START)
|
|
PARSE_FAIL(p,
|
|
"parse failed - entered notification elem unexpectedely");
|
|
for (i = 0; attr[i]; i += 2) {
|
|
const char *errstr;
|
|
if (strcmp("xmlns", attr[i]) == 0 &&
|
|
strcmp(RRDP_XMLNS, attr[i + 1]) == 0) {
|
|
has_xmlns = 1;
|
|
continue;
|
|
}
|
|
if (strcmp("session_id", attr[i]) == 0 &&
|
|
valid_uuid(attr[i + 1])) {
|
|
nxml->session_id = xstrdup(attr[i + 1]);
|
|
continue;
|
|
}
|
|
if (strcmp("version", attr[i]) == 0) {
|
|
nxml->version = strtonum(attr[i + 1],
|
|
1, MAX_VERSION, &errstr);
|
|
if (errstr == NULL)
|
|
continue;
|
|
}
|
|
if (strcmp("serial", attr[i]) == 0) {
|
|
nxml->serial = strtonum(attr[i + 1],
|
|
1, LLONG_MAX, &errstr);
|
|
if (errstr == NULL)
|
|
continue;
|
|
}
|
|
PARSE_FAIL(p, "parse failed - non conforming "
|
|
"attribute '%s' found in notification elem", attr[i]);
|
|
}
|
|
if (!(has_xmlns && nxml->version && nxml->session_id && nxml->serial))
|
|
PARSE_FAIL(p, "parse failed - incomplete "
|
|
"notification attributes");
|
|
|
|
/* Limit deltas to the ones which matter for us. */
|
|
if (nxml->min_serial == 0 && nxml->serial > MAX_RRDP_DELTAS)
|
|
nxml->min_serial = nxml->serial - MAX_RRDP_DELTAS;
|
|
|
|
nxml->scope = NOTIFICATION_SCOPE_NOTIFICATION;
|
|
}
|
|
|
|
static void
|
|
end_notification_elem(struct notification_xml *nxml)
|
|
{
|
|
XML_Parser p = nxml->parser;
|
|
|
|
if (nxml->scope != NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT)
|
|
PARSE_FAIL(p, "parse failed - exited notification "
|
|
"elem unexpectedely");
|
|
nxml->scope = NOTIFICATION_SCOPE_END;
|
|
|
|
if (!check_delta(nxml))
|
|
PARSE_FAIL(p, "parse failed - delta list has holes");
|
|
}
|
|
|
|
static void
|
|
start_snapshot_elem(struct notification_xml *nxml, const char **attr)
|
|
{
|
|
XML_Parser p = nxml->parser;
|
|
int i, hasUri = 0, hasHash = 0;
|
|
|
|
if (nxml->scope != NOTIFICATION_SCOPE_NOTIFICATION)
|
|
PARSE_FAIL(p,
|
|
"parse failed - entered snapshot elem unexpectedely");
|
|
for (i = 0; attr[i]; i += 2) {
|
|
if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) {
|
|
if (valid_uri(attr[i + 1], strlen(attr[i + 1]),
|
|
HTTPS_PROTO) &&
|
|
valid_origin(attr[i + 1], nxml->notifyuri)) {
|
|
nxml->snapshot_uri = xstrdup(attr[i + 1]);
|
|
continue;
|
|
}
|
|
}
|
|
if (strcmp("hash", attr[i]) == 0 && hasHash++ == 0) {
|
|
if (hex_decode(attr[i + 1], nxml->snapshot_hash,
|
|
sizeof(nxml->snapshot_hash)) == 0)
|
|
continue;
|
|
}
|
|
PARSE_FAIL(p, "parse failed - non conforming "
|
|
"attribute '%s' found in snapshot elem", attr[i]);
|
|
}
|
|
if (hasUri != 1 || hasHash != 1)
|
|
PARSE_FAIL(p, "parse failed - incomplete snapshot attributes");
|
|
|
|
nxml->scope = NOTIFICATION_SCOPE_SNAPSHOT;
|
|
}
|
|
|
|
static void
|
|
end_snapshot_elem(struct notification_xml *nxml)
|
|
{
|
|
XML_Parser p = nxml->parser;
|
|
|
|
if (nxml->scope != NOTIFICATION_SCOPE_SNAPSHOT)
|
|
PARSE_FAIL(p, "parse failed - exited snapshot "
|
|
"elem unexpectedely");
|
|
nxml->scope = NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT;
|
|
}
|
|
|
|
static void
|
|
start_delta_elem(struct notification_xml *nxml, const char **attr)
|
|
{
|
|
XML_Parser p = nxml->parser;
|
|
int i, hasUri = 0, hasHash = 0;
|
|
const char *delta_uri = NULL;
|
|
char delta_hash[SHA256_DIGEST_LENGTH];
|
|
long long delta_serial = 0;
|
|
|
|
if (nxml->scope != NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT)
|
|
PARSE_FAIL(p, "parse failed - entered delta "
|
|
"elem unexpectedely");
|
|
for (i = 0; attr[i]; i += 2) {
|
|
if (strcmp("uri", attr[i]) == 0 && hasUri++ == 0) {
|
|
if (valid_uri(attr[i + 1], strlen(attr[i + 1]),
|
|
HTTPS_PROTO) &&
|
|
valid_origin(attr[i + 1], nxml->notifyuri)) {
|
|
delta_uri = attr[i + 1];
|
|
continue;
|
|
}
|
|
}
|
|
if (strcmp("hash", attr[i]) == 0 && hasHash++ == 0) {
|
|
if (hex_decode(attr[i + 1], delta_hash,
|
|
sizeof(delta_hash)) == 0)
|
|
continue;
|
|
}
|
|
if (strcmp("serial", attr[i]) == 0 && delta_serial == 0) {
|
|
const char *errstr;
|
|
|
|
delta_serial = strtonum(attr[i + 1],
|
|
1, LLONG_MAX, &errstr);
|
|
if (errstr == NULL)
|
|
continue;
|
|
}
|
|
PARSE_FAIL(p, "parse failed - non conforming "
|
|
"attribute '%s' found in delta elem", attr[i]);
|
|
}
|
|
/* Only add to the list if we are relevant */
|
|
if (hasUri != 1 || hasHash != 1 || delta_serial == 0)
|
|
PARSE_FAIL(p, "parse failed - incomplete delta attributes");
|
|
|
|
/* Delta serial must be smaller or equal to the notification serial */
|
|
if (nxml->serial < delta_serial)
|
|
PARSE_FAIL(p, "parse failed - bad delta serial");
|
|
|
|
/* optimisation, add only deltas that could be interesting */
|
|
if (nxml->min_serial < delta_serial) {
|
|
if (add_delta(nxml, delta_uri, delta_hash, delta_serial) == 0)
|
|
PARSE_FAIL(p, "parse failed - adding delta failed");
|
|
}
|
|
|
|
nxml->scope = NOTIFICATION_SCOPE_DELTA;
|
|
}
|
|
|
|
static void
|
|
end_delta_elem(struct notification_xml *nxml)
|
|
{
|
|
XML_Parser p = nxml->parser;
|
|
|
|
if (nxml->scope != NOTIFICATION_SCOPE_DELTA)
|
|
PARSE_FAIL(p, "parse failed - exited delta elem unexpectedely");
|
|
nxml->scope = NOTIFICATION_SCOPE_NOTIFICATION_POST_SNAPSHOT;
|
|
}
|
|
|
|
static void
|
|
notification_xml_elem_start(void *data, const char *el, const char **attr)
|
|
{
|
|
struct notification_xml *nxml = data;
|
|
XML_Parser p = nxml->parser;
|
|
|
|
/*
|
|
* Can only enter here once as we should have no ways to get back to
|
|
* START scope
|
|
*/
|
|
if (strcmp("notification", el) == 0)
|
|
start_notification_elem(nxml, attr);
|
|
/*
|
|
* Will enter here multiple times, BUT never nested. will start
|
|
* collecting character data in that handler
|
|
* mem is cleared in end block, (TODO or on parse failure)
|
|
*/
|
|
else if (strcmp("snapshot", el) == 0)
|
|
start_snapshot_elem(nxml, attr);
|
|
else if (strcmp("delta", el) == 0)
|
|
start_delta_elem(nxml, attr);
|
|
else
|
|
PARSE_FAIL(p, "parse failed - unexpected elem exit found");
|
|
}
|
|
|
|
static void
|
|
notification_xml_elem_end(void *data, const char *el)
|
|
{
|
|
struct notification_xml *nxml = data;
|
|
XML_Parser p = nxml->parser;
|
|
|
|
if (strcmp("notification", el) == 0)
|
|
end_notification_elem(nxml);
|
|
else if (strcmp("snapshot", el) == 0)
|
|
end_snapshot_elem(nxml);
|
|
else if (strcmp("delta", el) == 0)
|
|
end_delta_elem(nxml);
|
|
else
|
|
PARSE_FAIL(p, "parse failed - unexpected elem exit found");
|
|
}
|
|
|
|
static void
|
|
notification_doctype_handler(void *data, const char *doctypeName,
|
|
const char *sysid, const char *pubid, int subset)
|
|
{
|
|
struct notification_xml *nxml = data;
|
|
XML_Parser p = nxml->parser;
|
|
|
|
PARSE_FAIL(p, "parse failed - DOCTYPE not allowed");
|
|
}
|
|
|
|
struct notification_xml *
|
|
new_notification_xml(XML_Parser p, struct rrdp_session *repository,
|
|
struct rrdp_session *current, const char *notifyuri)
|
|
{
|
|
struct notification_xml *nxml;
|
|
|
|
if ((nxml = calloc(1, sizeof(*nxml))) == NULL)
|
|
err(1, "%s", __func__);
|
|
TAILQ_INIT(&(nxml->delta_q));
|
|
nxml->parser = p;
|
|
nxml->repository = repository;
|
|
nxml->current = current;
|
|
nxml->notifyuri = notifyuri;
|
|
nxml->min_serial = delta_parse(repository, 0, NULL);
|
|
|
|
XML_SetElementHandler(nxml->parser, notification_xml_elem_start,
|
|
notification_xml_elem_end);
|
|
XML_SetUserData(nxml->parser, nxml);
|
|
XML_SetDoctypeDeclHandler(nxml->parser, notification_doctype_handler,
|
|
NULL);
|
|
|
|
return nxml;
|
|
}
|
|
|
|
static void
|
|
free_delta_queue(struct notification_xml *nxml)
|
|
{
|
|
while (!TAILQ_EMPTY(&nxml->delta_q)) {
|
|
struct delta_item *d = TAILQ_FIRST(&nxml->delta_q);
|
|
TAILQ_REMOVE(&nxml->delta_q, d, q);
|
|
free_delta(d);
|
|
}
|
|
}
|
|
|
|
void
|
|
free_notification_xml(struct notification_xml *nxml)
|
|
{
|
|
if (nxml == NULL)
|
|
return;
|
|
|
|
free(nxml->session_id);
|
|
free(nxml->snapshot_uri);
|
|
free_delta_queue(nxml);
|
|
free(nxml);
|
|
}
|
|
|
|
/*
|
|
* Collect a list of deltas to store in the repository state.
|
|
*/
|
|
static void
|
|
notification_collect_deltas(struct notification_xml *nxml)
|
|
{
|
|
struct delta_item *d;
|
|
long long keep_serial = 0;
|
|
size_t cur_idx = 0, max_deltas;
|
|
char *hash;
|
|
|
|
max_deltas =
|
|
sizeof(nxml->current->deltas) / sizeof(nxml->current->deltas[0]);
|
|
|
|
if (nxml->serial > (long long)max_deltas)
|
|
keep_serial = nxml->serial - max_deltas + 1;
|
|
|
|
TAILQ_FOREACH(d, &nxml->delta_q, q) {
|
|
if (d->serial >= keep_serial) {
|
|
assert(cur_idx < max_deltas);
|
|
hash = hex_encode(d->hash, sizeof(d->hash));
|
|
if (asprintf(&nxml->current->deltas[cur_idx++],
|
|
"%lld %s", d->serial, hash) == -1)
|
|
err(1, NULL);
|
|
free(hash);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Validate the delta list with the information from the repository state.
|
|
* Remove all obsolete deltas so that the list starts with the delta with
|
|
* serial nxml->repository->serial + 1.
|
|
* Returns 1 if all deltas were valid and 0 on failure.
|
|
*/
|
|
static int
|
|
notification_check_deltas(struct notification_xml *nxml)
|
|
{
|
|
struct delta_item *d, *nextd;
|
|
char *hash, *exp_hash;
|
|
long long exp_serial, new_serial;
|
|
size_t exp_idx = 0;
|
|
|
|
exp_serial = delta_parse(nxml->repository, exp_idx++, &exp_hash);
|
|
new_serial = nxml->repository->serial + 1;
|
|
|
|
/* compare hash of delta against repository state info */
|
|
TAILQ_FOREACH_SAFE(d, &nxml->delta_q, q, nextd) {
|
|
while (exp_serial != 0 && exp_serial < d->serial) {
|
|
exp_serial = delta_parse(nxml->repository,
|
|
exp_idx++, &exp_hash);
|
|
}
|
|
|
|
if (d->serial == exp_serial) {
|
|
hash = hex_encode(d->hash, sizeof(d->hash));
|
|
if (strcmp(hash, exp_hash) != 0) {
|
|
warnx("%s: %s#%lld unexpected delta "
|
|
"mutation (expected %s, got %s)",
|
|
nxml->notifyuri, nxml->session_id,
|
|
exp_serial, hash, exp_hash);
|
|
free(hash);
|
|
return 0;
|
|
}
|
|
free(hash);
|
|
exp_serial = delta_parse(nxml->repository,
|
|
exp_idx++, &exp_hash);
|
|
}
|
|
|
|
/* is this delta needed? */
|
|
if (d->serial < new_serial) {
|
|
TAILQ_REMOVE(&nxml->delta_q, d, q);
|
|
free_delta(d);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Finalize notification step, decide if a delta update is possible
|
|
* if either the session_id changed or the delta files fail to cover
|
|
* all the steps up to the new serial fall back to a snapshot.
|
|
* Return SNAPSHOT or DELTA for snapshot or delta processing.
|
|
* Return NOTIFICATION if repository is up to date.
|
|
*/
|
|
enum rrdp_task
|
|
notification_done(struct notification_xml *nxml, char *last_mod)
|
|
{
|
|
nxml->current->last_mod = last_mod;
|
|
nxml->current->session_id = xstrdup(nxml->session_id);
|
|
notification_collect_deltas(nxml);
|
|
|
|
/* check the that the session_id was valid and still the same */
|
|
if (nxml->repository->session_id == NULL ||
|
|
strcmp(nxml->session_id, nxml->repository->session_id) != 0)
|
|
goto snapshot;
|
|
|
|
/* if repository serial is 0 fall back to snapshot */
|
|
if (nxml->repository->serial == 0)
|
|
goto snapshot;
|
|
|
|
/* check that all needed deltas are available and valid */
|
|
if (!notification_check_deltas(nxml))
|
|
goto snapshot;
|
|
|
|
if (nxml->repository->serial > nxml->serial)
|
|
warnx("%s: serial number decreased from %lld to %lld",
|
|
nxml->notifyuri, nxml->repository->serial, nxml->serial);
|
|
|
|
/* if our serial is equal or plus 2, the repo is up to date */
|
|
if (nxml->repository->serial >= nxml->serial &&
|
|
nxml->repository->serial - nxml->serial <= 2) {
|
|
nxml->current->serial = nxml->repository->serial;
|
|
return NOTIFICATION;
|
|
}
|
|
|
|
/* it makes no sense to process too many deltas */
|
|
if (nxml->serial - nxml->repository->serial > MAX_RRDP_DELTAS)
|
|
goto snapshot;
|
|
|
|
/* no deltas queued */
|
|
if (TAILQ_EMPTY(&nxml->delta_q))
|
|
goto snapshot;
|
|
|
|
/* first possible delta is no match */
|
|
if (nxml->repository->serial + 1 != TAILQ_FIRST(&nxml->delta_q)->serial)
|
|
goto snapshot;
|
|
|
|
/* update via delta possible */
|
|
nxml->current->serial = nxml->repository->serial;
|
|
nxml->repository->serial = nxml->serial;
|
|
return DELTA;
|
|
|
|
snapshot:
|
|
/* update via snapshot download */
|
|
free_delta_queue(nxml);
|
|
nxml->current->serial = nxml->serial;
|
|
return SNAPSHOT;
|
|
}
|
|
|
|
const char *
|
|
notification_get_next(struct notification_xml *nxml, char *hash, size_t hlen,
|
|
enum rrdp_task task)
|
|
{
|
|
struct delta_item *d;
|
|
|
|
switch (task) {
|
|
case SNAPSHOT:
|
|
assert(hlen == sizeof(nxml->snapshot_hash));
|
|
memcpy(hash, nxml->snapshot_hash, hlen);
|
|
/*
|
|
* Ensure that the serial is correct in case a previous
|
|
* delta request failed.
|
|
*/
|
|
nxml->current->serial = nxml->serial;
|
|
return nxml->snapshot_uri;
|
|
case DELTA:
|
|
/* first bump serial, then use first delta */
|
|
nxml->current->serial += 1;
|
|
d = TAILQ_FIRST(&nxml->delta_q);
|
|
assert(d->serial == nxml->current->serial);
|
|
assert(hlen == sizeof(d->hash));
|
|
memcpy(hash, d->hash, hlen);
|
|
return d->uri;
|
|
default:
|
|
errx(1, "%s: bad task", __func__);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Pop first element from the delta queue. Return non-0 if this was the last
|
|
* delta to fetch.
|
|
*/
|
|
int
|
|
notification_delta_done(struct notification_xml *nxml)
|
|
{
|
|
struct delta_item *d;
|
|
|
|
d = TAILQ_FIRST(&nxml->delta_q);
|
|
assert(d->serial == nxml->current->serial);
|
|
TAILQ_REMOVE(&nxml->delta_q, d, q);
|
|
free_delta(d);
|
|
|
|
assert(!TAILQ_EMPTY(&nxml->delta_q) ||
|
|
nxml->serial == nxml->current->serial);
|
|
return TAILQ_EMPTY(&nxml->delta_q);
|
|
}
|
|
|
|
/* Used in regress. */
|
|
void
|
|
log_notification_xml(struct notification_xml *nxml)
|
|
{
|
|
struct delta_item *d;
|
|
char *hash;
|
|
|
|
logx("session_id: %s, serial: %lld", nxml->session_id, nxml->serial);
|
|
logx("snapshot_uri: %s", nxml->snapshot_uri);
|
|
hash = hex_encode(nxml->snapshot_hash, sizeof(nxml->snapshot_hash));
|
|
logx("snapshot hash: %s", hash);
|
|
free(hash);
|
|
|
|
TAILQ_FOREACH(d, &nxml->delta_q, q) {
|
|
logx("delta serial %lld uri: %s", d->serial, d->uri);
|
|
hash = hex_encode(d->hash, sizeof(d->hash));
|
|
logx("delta hash: %s", hash);
|
|
free(hash);
|
|
}
|
|
}
|