PERFORCE change 123568 for review

Fredrik Lindberg fli at FreeBSD.org
Mon Jul 16 01:12:58 UTC 2007


http://perforce.freebsd.org/chv.cgi?CH=123568

Change 123568 by fli at fli_nexus on 2007/07/16 01:12:45

	- Revisit/fix record and resource construction, they now
	  properly follow the system state.
	- Introduce a "pointer" concept where the resource data of
	  one record can point to name of another record. This useful
	  for PTR records that should follow a coresponding A/AAAA
	  record automatically.
	- Add record probing and annoucing. When probing for a new
	  name a "probe context" is created to which records are
	  added, upon collision the record is removed from the context
	  which will self-destruct when no records are left.
	  A probe is initiated when ever a record changes name.
	  The "announce context" works in a similar way.
	- Style fixes.

Affected files ...

.. //depot/projects/soc2007/fli-mdns_sd/mdnsd/dbrec.c#2 edit
.. //depot/projects/soc2007/fli-mdns_sd/mdnsd/dbrec.h#1 add

Differences ...

==== //depot/projects/soc2007/fli-mdns_sd/mdnsd/dbrec.c#2 (text+ko) ====

@@ -26,14 +26,33 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "mdnsd.h"
+#include "objalloc.h"
+#include "output.h"
 #include "record.h"
 #include "log.h"
 #include "var.h"
 
 static void record_type_del_cb(struct record_type *, void *);
+static void rec_update(struct dbr_rec *, struct vt_data *, size_t);
+static void rec_var_update(void *);
+static inline void rec_pastop(struct dbr_rec *);
+static void rec_del(struct dbr_rec *);
+static void res_update(struct dbr_res *, wchar_t **, size_t);
+static void res_var_update(void *);
+static void res_del(struct dbr_res *);
+
+static int dbr_pac_add(struct dbr_pac *, struct dbr_rec *, int);
+static int dbr_pac_del(struct dbr_rec *, int);
+static void dbr_pac_start(struct dbr_pac *, int, int, void *);
+static int probe_step(const struct event_tmr *, const ev_arg);
+static int an_step(const struct event_tmr *, const ev_arg);
 
+/*
+ * Initialize a database record system
+ */
 void
 dbr_init(struct dbr *dbr, void *context)
 {
@@ -41,6 +60,7 @@
 
 	dbr->dbr_ctx = context;
 	MDNS_INIT_SET(dbr, dbr_magic);
+	hashtbl_init(&dbr->dbr_ident, 8, 512, 3);
 
 	var_init(&dbr->dbr_vars, mif->mif_glob->g_evl, dbr);
 	/* We only do the IN class */
@@ -58,6 +78,9 @@
 	dbr_del(dr);
 }
 
+/*
+ * Destroy a database record system
+ */
 void
 dbr_destroy(struct dbr *dbr)
 {
@@ -69,179 +92,211 @@
 	records_foreach(&dbr->dbr_recs, record_del_cb, NULL);
 	records_destroy(&dbr->dbr_recs);
 	var_destroy(&dbr->dbr_vars, mif->mif_glob->g_evl);
+	hashtbl_destroy(&dbr->dbr_ident);
 	MDNS_INIT_UNSET(dbr, dbr_magic);
+
 	dprintf(DEBUG_DBR, "Database records destroyed");
 }
 
-static struct dbr_res *
-clone_res(struct dbr_res *orig)
+/*
+ * Clone a record
+ */
+static struct dbr_rec *
+clone_rec(struct dbr_rec *orig)
+{
+	struct dbr_rec *dr;
+
+	dr = malloc(sizeof(struct dbr_rec));
+	if (dr == NULL)
+		return (NULL);
+	memcpy(dr, orig, sizeof(struct dbr_rec));
+	dr->dr_flags |= DR_CLONE;	
+	TAILQ_INSERT_TAIL(&orig->dr_clone.head, dr, dr_clone.next);
+	dprintf(DEBUG_DBR, "Database record cloned dr=%x, source=%x", dr, orig);
+	return (dr);
+}
+
+/*
+ * Clone all resources from `orig' onto `dr'
+ */
+static void
+clone_rec_res(struct dbr_rec *orig, struct dbr_rec *dr)
 {
+	struct record *r;
+	struct record_type *rt;
+	struct record_res *rr;
 	struct dbr_res *ds;
+	struct dbr_rec *dr2;
+	void *res;
+	int ptr;
 
-	ds = malloc(sizeof(struct dbr_res));
-	if (ds == NULL)
-		return (NULL);
-	memcpy(ds, orig, sizeof(struct dbr_res));
-	ds->ds_data = NULL;
-	ds->ds_flags |= DS_CLONE;
-	TAILQ_INSERT_TAIL(&orig->ds_clone.head, ds, ds_clone.next);
-	dprintf(DEBUG_DBR, "Database resource cloned ds=%x, source=%x", ds, orig);
-	return (ds);
+	MDNS_INIT_ASSERT(dr, dr_magic);
+	MDNS_INIT_ASSERT(orig, dr_magic);
+	r = &orig->dr_rec;
+	MDNS_INIT_ASSERT(r, r_magic);
+
+	record_foreach(rt, r) {
+		record_type_foreach(rr, rt) {
+			ds = record_res_getparent(rr);
+			ptr = ds->ds_flags & DS_POINTER;
+			if (ptr) {
+				dr2 = ds->ds_vdata.rec;
+				res = dr2->dr_ident;
+			}
+			else
+				res = ds->ds_vdata.wp;
+
+			dbr_res_add(dr, rt->rt_type, ds->ds_ttl, res, ptr);
+		}
+	}
+	dprintf(DEBUG_DBR, "Cloned resources from %x to %x", orig, dr);
 }
 
 /*
- * Variable update handler to resource data, will re-expand vaiables
- * and update affected resources.
- * This is an asynchronous callback routine which is called from the
- * variable event system.
+ * Update a database record
+ *   dr  - Database record
+ *   vtd - Arrays of expanded names
+ *   vtd_len - Array length
  *
- *   ptr - Opaque pointer passed to var_reg(), to this routine it's
- *         always of type dbr_res {}
+ * A record clone will be created for each excessive member of `vtd', if
+ * there currently are more clones than the length of the provided array,
+ * then those clones are removed.
  */
 static void
-res_var_update(void *ptr)
+rec_update(struct dbr_rec *dr, struct vt_data *vtd, size_t vtd_len)
 {
-	struct dbr_res *ds, *ds_clone, *ds_tmp;
-	struct dbr_type *dt;
-	struct dbr_rec *dr;
+	struct dbr_rec *dr_clone, *dr_tmp;
+	struct dbr_res *ds, *ds2;
 	struct dbr *dbr;
-	struct record_type *rt;
-	struct record_res *rr;
 	struct record *r;
-	struct vt_data *vtd;
-	size_t vtd_len, rlen;
 	uint32_t i;
 	int32_t diff;
-	char *p;
+	wchar_t **names;
+	char *nam;
 
-	ds = ptr;
-	MDNS_INIT_ASSERT(ds, ds_magic);
-	dt = ds->ds_type;
-	MDNS_INIT_ASSERT(dt, dt_magic);
-	dr = dt->dt_rec;
 	MDNS_INIT_ASSERT(dr, dr_magic);
 	dbr = dr->dr_dbr;
 	MDNS_INIT_ASSERT(dbr, dbr_magic);
 
-	rt = &dt->dt_type;
+	assert(dr == dr->dr_chead);
 
-	/* TODO: locking */
+	/* Stop pending probes/announces */
+	rec_pastop(dr);
 
-	vtd = var_expand(&dbr->dbr_vars, ds->ds_vdata, &vtd_len);
+	/*
+	 * If the provided array length is 0 there are no available
+	 * names, invalidate record and remove all clones.
+	 */
+	if (vtd_len == 0) {
+		if (dr->dr_name != NULL)
+			free(dr->dr_name);
+		dr->dr_name = NULL;
+		dr->dr_flags |= DR_INVALID;
+		dr->dr_flags &= ~DR_OK;
 
-	if (vtd_len == 0) {
-		if (!(ds->ds_flags & DS_INVALID)) {
-			/* TODO: annonuce old res with ttl 0 */
-			free(ds->ds_data);	
+		TAILQ_FOREACH_SAFE(dr_clone, &dr->dr_clone.head,
+		    dr_clone.next, dr_tmp) {
+			rec_del(dr_clone);
 		}
-		record_res_setdata(&ds->ds_res, NULL, 0);
-		ds->ds_flags |= DS_INVALID;
-		dprintf(DEBUG_DBR, "Removing clones on %x, clones=%d",
-		    ds, ds->ds_clones);
-		TAILQ_FOREACH(ds_clone, &ds->ds_clone.head, ds_clone.next) {
-			TAILQ_REMOVE(&ds->ds_clone.head, ds_clone, ds_clone.next);
-			record_res_del(&ds_clone->ds_res);
+
+		/*
+		 * Invalidate all database resources that are using this
+		 * record name as their data source.
+		 */
+		TAILQ_FOREACH(ds, &dr->dr_res_ptr, ds_ptr_next) {
+			ds->ds_flags |= DS_INVALID;
 			free(ds->ds_data);
-			free(ds_clone);
-			ds->ds_clones--;
+			ds->ds_data = NULL;
 		}
-		goto out;
+		return;
+	}
+
+	i = 1;
+	nam = mdns_name_encode(vtd[0].vtd_str, vtd[0].vtd_len, MDNS_ENC_AUTO);
+	r = &dr->dr_rec;
+	if (dr->dr_name != NULL)
+		free(dr->dr_name);
+	record_setname(r, nam);
+
+	free(nam);
+	dr->dr_name = vtd[0].vtd_str;
+	dr->dr_flags &= ~(DR_OK | DR_INVALID);
+
+	/*
+	 * Replace names as long as there are enough clones
+	 * and results available.
+	 */
+	TAILQ_FOREACH(dr_clone, &dr->dr_clone.head, dr_clone.next) {
+		if ((i - 1) == dr->dr_clones || i == vtd_len)
+			break;
+		r = &dr_clone->dr_rec;
+		nam = mdns_name_encode(vtd[i].vtd_str, vtd[i].vtd_len,
+		    MDNS_ENC_AUTO);
+		record_setname(r, nam);
+		free(nam);
+		if (dr_clone->dr_name != NULL)
+			free(dr_clone->dr_name);
+		dr_clone->dr_name = vtd[i].vtd_str;
+		dr_clone->dr_flags &= ~DR_OK;
+		i++;
 	}
-	else if (vtd_len >= 1) {
-		if (!(ds->ds_flags & DS_INVALID))
-			free(ds->ds_data);
-		ds->ds_data = vtd[0].vtd_str;
-		p = mdns_res_encode(mdns_c_in, rt->rt_type, ds->ds_data,
-		    MDNS_ENC_WCHAR, vtd[0].vtd_len, &rlen);
-		record_res_setdata(&ds->ds_res, p, rlen);
-		i = 1;
 
-		/*
-		 * Replace resource data as long as there is enough clones
-		 * and results available.
-		 */
-		TAILQ_FOREACH(ds_clone, &ds->ds_clone.head, ds_clone.next) {
-			if (i == ds->ds_clones || i == vtd_len)
-				break;
-			free(ds_clone->ds_data);
-			ds->ds_data = vtd[i].vtd_str;
-			p = mdns_res_encode(mdns_c_in, rt->rt_type, ds->ds_data,
-			    MDNS_ENC_WCHAR, vtd[i].vtd_len, &rlen);
-			record_res_setdata(&ds->ds_res, p, rlen);
+	/* More results than clones, expand */
+	if (vtd_len > i) {
+		diff = vtd_len - i;
+		while (diff-- > 0) {
+			dr_clone = clone_rec(dr);
+			dr_clone->dr_clones = 0;
+			dr->dr_clones++;
+			r = &dr_clone->dr_rec;
+			nam = mdns_name_encode(vtd[i].vtd_str, vtd[i].vtd_len,
+				MDNS_ENC_AUTO);
+			record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, nam);
+			record_setparent(r, dr_clone);
+			clone_rec_res(dr, dr_clone);
+			free(nam);
+			dr_clone->dr_name = vtd[i].vtd_str;
 			i++;
 		}
+	}
+	/* More clones than results, shrink */
+	else if (dr->dr_clones >= i) {
+		diff = dr->dr_clones - i;
+		while (diff-- >= 0) {
+			dr_tmp = TAILQ_LAST(&dr->dr_clone.head, recclone_head);
+			rec_del(dr_tmp);
+		}
+	}
 
-		/* More results than clones, expand */
-		if (vtd_len > i) {
-			diff = vtd_len - i;
-			r = ds->ds_res.rr_type->rt_record;
-			MDNS_INIT_ASSERT(r, r_magic);
-			while (diff-- > 0) {
-				ds_clone = clone_res(ds);
-				ds_clone->ds_clones = 0;
-				ds->ds_clones++;
-				ds_clone->ds_data = vtd[i].vtd_str;
-				rr = &ds_clone->ds_res;
-				p = mdns_res_encode(mdns_c_in, rt->rt_type, ds_clone->ds_data,
-				    MDNS_ENC_WCHAR, vtd[i].vtd_len, &rlen);
-				record_res_add(r, &rr, RECORD_NOALLOC, rt->rt_type, p, rlen);
-				record_res_setparent(&ds_clone->ds_res, ds);
-				i++;
-			}
-		}
-		/* More clones than results, shrink */
-		else if (ds->ds_clones >= i) {
-			diff = ds->ds_clones - i;
-			while (diff-- >= 0) {
-				ds_tmp = TAILQ_LAST(&ds->ds_clone.head, clone_head);
-				TAILQ_REMOVE(&ds->ds_clone.head, ds_tmp, ds_clone.next);
-				record_res_del(&ds_tmp->ds_res);
-				free(ds_tmp);
-				ds->ds_clones--;
-			}
-		}
+	/* Update resources that points to this record */
+	names = malloc(sizeof(wchar_t *) * vtd_len);
+	TAILQ_FOREACH_SAFE(ds, &dr->dr_res_ptr, ds_ptr_next, ds2) {
+		for (i = 0; i < vtd_len; i++)
+			names[i] = _wcsdup(vtd[i].vtd_str);
+		res_update(ds, names, vtd_len);
 	}
+	free(names);
 
-	ds->ds_flags &= ~DS_INVALID;
-	/* TODO: cache flush annonuce */
-	free(vtd);
 	dprintf(DEBUG_DBR,
-	    "Database resource %x updated, type=%d, ttl=%d, clones=%d",
-		ds, rt->rt_type, ds->ds_ttl, vtd_len-1);
-	return;
-out:
-	var_vtdfree(vtd, vtd_len);
-	dprintf(DEBUG_DBR, "Database resource %x marked as invalid", ds);
+	    "Database record r=%x, (%ls) updated, flags=%x, clones=%d",
+	    dr, dr->dr_names[dr->dr_cur], dr->dr_flags, dr->dr_clones);
 }
 
-static struct dbr_rec *
-clone_rec(struct dbr_rec *orig)
-{
-	struct dbr_rec *dr;
-
-	dr = malloc(sizeof(struct dbr_rec));
-	if (dr == NULL)
-		return (NULL);
-	memcpy(dr, orig, sizeof(struct dbr_rec));
-	dr->dr_flags |= DR_CLONE;	
-	TAILQ_INSERT_TAIL(&orig->dr_clone.head, dr, dr_clone.next);
-	dprintf(DEBUG_DBR, "Database record cloned dr=%x, source=%x", dr, orig);
-	return (dr);
-}
-
-
+/*
+ * Variable update handler to record names, callback routine from
+ * variable system.
+ */
 static void
 rec_var_update(void *ptr)
 {
-	struct dbr_rec *dr, *dr_clone, *dr_tmp;
 	struct dbr *dbr;
+	struct dbr_rec *dr, *dr2;
+	struct dbr_pac *pac;
+	struct dbr_res *ds;
 	struct record *r;
 	struct vt_data *vtd;
 	size_t vtd_len;
 	wchar_t *wp;
-	char *nam;
-	uint32_t i;
-	int32_t diff;
 
 	dr = ptr;
 	MDNS_INIT_ASSERT(dr, dr_magic);
@@ -249,91 +304,58 @@
 	MDNS_INIT_ASSERT(dbr, dbr_magic);
 	r = &dr->dr_rec;
 
+	assert(dr == dr->dr_chead);
+
 	wp = dr->dr_names[dr->dr_cur];
 	vtd = var_expand(&dbr->dbr_vars, wp, &vtd_len);
-	if (vtd_len >= 1) {
-		nam = mdns_name_encode(vtd[0].vtd_str, vtd[0].vtd_len,
-		    MDNS_ENC_AUTO);
-		record_setname(r, nam);
-		free(nam);
-		i = 1;
+	rec_update(dr, vtd, vtd_len);
+	free(vtd);
 
-		/*
-		 * Replace names as long as there are enough clones
-		 * and results available.
-		 */
-		TAILQ_FOREACH(dr_clone, &dr->dr_clone.head, dr_clone.next) {
-			if (i == dr->dr_clones || i == vtd_len)
-				break;
-			nam = mdns_name_encode(vtd[i].vtd_str, vtd[i].vtd_len,
-			    MDNS_ENC_AUTO);
-			record_setname(r, nam);
-			free(nam);
-			i++;
-		}
-
-		/* More results than clones, expand */
-		if (vtd_len > i) {
-			diff = vtd_len - i;
-			while (diff-- > 0) {
-				dr_clone = clone_rec(dr);
-				dr_clone->dr_clones = 0;
-				dr->dr_clones++;
-				r = &dr_clone->dr_rec;
-				nam = mdns_name_encode(vtd[i].vtd_str, vtd[i].vtd_len,
-				    MDNS_ENC_AUTO);
-				record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, nam);
-				record_setparent(r, dr_clone);
-// FIXME clone all resource data
-				free(nam);
-				i++;
-			}
-		}
-		/* More clones than results, shrink */
-		else if (dr->dr_clones >= i) {
-			diff = dr->dr_clones - i;
-			while (diff-- >= 0) {
-				dr_tmp = TAILQ_LAST(&dr->dr_clone.head, recclone_head);
-				TAILQ_REMOVE(&dr->dr_clone.head, dr_tmp, dr_clone.next);
-				r = &dr_tmp->dr_rec;
-				record_foreach_type(r, record_type_del_cb, NULL);
-				record_release(r);
-				MDNS_INIT_UNSET(dr_tmp, dr_magic);
-				free(dr_tmp);
-				dr->dr_clones--;
-			}
-		}
-
-		if (dr->dr_type == DR_SHARED)
-			dr->dr_state = DR_OK;
-		else {
-			dr->dr_state = DR_TENTATIVE;
-			/* TODO: relaunch probe */
-		}
+	/*
+	 * Create a probe context and and affected records to it
+	 */
+	pac = dbr_pac_new(dbr, PAC_PROBE);
+	dbr_probe_add(pac, dr);
+	TAILQ_FOREACH(dr2, &dr->dr_clone.head, dr_clone.next) {
+		dbr_probe_add(pac, dr2);
 	}
-	else {
-		dr->dr_state = DR_INVALID;
+	/* Resources pointing to this record */
+	TAILQ_FOREACH(ds, &dr->dr_res_ptr, ds_ptr_next) {
+		MDNS_INIT_ASSERT(ds, ds_magic);
+		dr2 = ds->ds_type->dt_rec;
+		MDNS_INIT_ASSERT(dr2, dr_magic);
+		dbr_probe_add(pac, dr2);
 	}
-
-	if (vtd != NULL)
-		var_vtdfree(vtd, vtd_len);
-
-	dprintf(DEBUG_DBR,
-	    "Database record r=%x updated name=%ls, state=%d, clones=%d",
-	    dr, wp, dr->dr_state, dr->dr_clones);
+	dbr_probe_start(pac);
 }
 
+/*
+ * Add a new database record
+ *   dbr   - Record database
+ *   ident - Unique record set identifier
+ *   names - Array of un-expanded alternativ names (NULL-terminated)
+ *   shared  - Shared record?
+ *
+ */
 struct dbr_rec *
-dbr_add(struct dbr *dbr, wchar_t **names, int type)
+dbr_add(struct dbr *dbr, char *ident, wchar_t **names, int shared)
 {
 	wchar_t *wp;
 	struct vt_data *vtd;
 	size_t vtd_len;
-	struct dbr_rec *dr, *dr_clone;
+	struct dbr_rec *dr;
 	struct record *r;
-	char *nam;
 	int error;
-	uint32_t i, j;
+	uint32_t i;
+
+	dr = hashtbl_find(&dbr->dbr_ident, ident, strlen(ident));
+	if (dr != NULL) {
+		dprintf(DEBUG_DBR, "Record set %s does already exists dr=%x",
+		    ident, dr);
+		return (dr);
+	}
+	else if (names[0] == NULL)
+		return (NULL);
 
 	i = 0;
 	error = -1;
@@ -341,78 +363,61 @@
 	TAILQ_INIT(&dr->dr_clone.head);
 	dr->dr_clones = 0;
 	dr->dr_dbr = dbr;
+	dr->dr_name = NULL;
+	dr->dr_ident = strdup(ident);
 	dr->dr_names = names;
-	dr->dr_type = type;
 	dr->dr_cur = 0;
+	dr->dr_cols = 0;
+	dr->dr_col_ts = 0;
 	dr->dr_chead = dr;
+	dr->dr_flags = DR_INVALID;
+	r = &dr->dr_rec;
+	record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, ident);
+	record_setparent(r, dr);
+
+	if (shared)
+		dr->dr_flags |= DR_SHARED;
+	TAILQ_INIT(&dr->dr_res_ptr);
 	MDNS_INIT_SET(dr, dr_magic);
-	r = &dr->dr_rec;
+
 	do {
 		wp = names[i++];
 		if (wp == NULL)
 			break;
 
 		vtd = var_expand(&dbr->dbr_vars, wp, &vtd_len);
-		if (vtd_len >= 1) {
-			nam = mdns_name_encode(vtd[0].vtd_str, vtd[0].vtd_len,
-			    MDNS_ENC_AUTO);
-			record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, nam);
-			record_setparent(r, dr);
-			free(nam);
-		}
-		error = var_reg(&dbr->dbr_vars, wp, rec_var_update, dr);
-		if (error != 0) {
-			dprintf(DEBUG_DBR, "Variable registration failed");
-			var_dereg(&dbr->dbr_vars, wp, dr);
-			if (vtd_len >= 1)
-				record_release(r);
+		if (vtd_len > 0) {
+			var_reg(&dbr->dbr_vars, wp, rec_var_update, dr);
+			rec_update(dr, vtd, vtd_len);
 		}
 		else {
-			dr->dr_cur = i - 1;
-			for (j = 1; j < vtd_len; j++) {
-				nam = mdns_name_encode(vtd[j].vtd_str, vtd[j].vtd_len,
-				    MDNS_ENC_AUTO);
-				dr_clone = clone_rec(dr);
-				dr->dr_clones++;
-				dr_clone->dr_chead = dr;
-				r = &dr_clone->dr_rec;
-				record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, nam);
-				record_setparent(r, dr_clone);
-				free(nam);
-			}
+			var_vtdfree(vtd, vtd_len);
 		}
+	} while (vtd_len == 0);
 
-		var_vtdfree(vtd, vtd_len);
-	} while (error != 0);
-
 	/*
 	 * If all alternative names fail to expand just set it to the
 	 * first name and mark the record as invalid.
 	 */
-	if (error != 0) {
+	if (vtd_len == 0) {
 		wp = names[0];
 		var_reg(&dbr->dbr_vars, wp, rec_var_update, dr);
-		dr->dr_state = DR_INVALID;
 		dr->dr_cur = 0;
-
-		/*
-		 * This is slightly "incorrect" as we add the unexpanded
-		 * name to the record database, but if we do not add it
-		 * we can't add any resources to this record.
-		 */
-		nam = mdns_name_encode(wp, wcslen(wp), MDNS_ENC_WCHAR);
-		record_get(&dbr->dbr_recs, &r, RECORD_NOALLOC, nam);
-		record_setparent(&dr->dr_rec, r);
 	}
 	else {
-		dr->dr_state = DR_TENTATIVE;
+		dr->dr_cur = i - 1;
 	}
 
-	if (type == DR_SHARED && dr->dr_state == DR_TENTATIVE)
-		dr->dr_state = DR_OK;
+	dr->dr_flags &= ~DR_OK;
+	hashtbl_add(&dbr->dbr_ident, dr->dr_ident, strlen(dr->dr_ident), dr, 0);
+
+	if ((dr->dr_flags & DR_SHARED) && !(dr->dr_flags & DR_INVALID))
+		dr->dr_flags |= DR_OK;
 
-	dprintf(DEBUG_DBR, "New database record dr=%x, name=%ls, clones=%d",
-	    dr, dr->dr_names[dr->dr_cur], dr->dr_clones);
+	dprintf(DEBUG_DBR,
+	    "New database record ident=%s, dr=%x, name=%ls, clones=%d, "
+	    "flags=%x", dr->dr_ident, dr, dr->dr_names[dr->dr_cur],
+	    dr->dr_clones, dr->dr_flags);
 	return (dr);
 }
 
@@ -434,20 +439,68 @@
 
 	MDNS_INIT_ASSERT(rt, rt_magic);
 	dt = record_type_getparent(rt);
-
 	record_type_foreach_res(rt, record_res_del_cb, NULL);
 	assert(record_type_refcnt(rt) == 0);
 	free(dt);
 }
 
+/*
+ * Free a database record and resources allocated to it
+ *   dr - Database record
+ */
+static void
+rec_del(struct dbr_rec *dr)
+{
+	struct dbr_rec *drh;
+	struct record *r;
+	wchar_t *wp;
+	int i;
+
+	MDNS_INIT_ASSERT(dr, dr_magic);
+	r = &dr->dr_rec;
+	drh = dr->dr_chead;
+	MDNS_INIT_ASSERT(drh, dr_magic);
+
+	if (dr->dr_flags & DR_PROBING)
+		dbr_probe_del(dr);
+	if (dr->dr_flags & DR_ANNOUNCE)
+		dbr_an_del(dr);
+
+	if (dr->dr_flags & DR_CLONE) {
+		TAILQ_REMOVE(&drh->dr_clone.head, dr, dr_clone.next);
+		drh->dr_clones--;
+	}
+	else {
+		i = 0;
+		do {
+			wp = dr->dr_names[i++];
+			if (wp != NULL)
+				free(wp);
+		} while(wp != NULL);
+		free(dr->dr_names);
+		free(dr->dr_ident);
+	}
+
+	record_foreach_type(r, record_type_del_cb, NULL);
+	record_release(r);
+	assert(record_refcnt(r) == 0);
+	if (!(dr->dr_flags & DR_INVALID))
+		free(dr->dr_name);
+	MDNS_INIT_UNSET(dr, dr_magic);
+	free(dr);
+}
+
+/*
+ * Remove a database record, any existing clones and also removes
+ * resources that are using this record name as their resource data.
+ *   dr - Database record
+ */
 void
 dbr_del(struct dbr_rec *dr)
 {
-	struct record *r;
 	struct dbr *dbr;
 	struct dbr_rec *dr2, *dr_tmp;
-	int i;
-	wchar_t *wp;
+	struct dbr_res *ds, *ds2;
 
 	MDNS_INIT_ASSERT(dr, dr_magic);
 	dbr = dr->dr_dbr;
@@ -455,58 +508,253 @@
 
 	dr = dr->dr_chead;	
 	var_dereg(&dbr->dbr_vars, dr->dr_names[dr->dr_cur], dr);
+	/*
+	 * Remove any resources that are using this records name
+	 * as their resource data.
+	 */
+	TAILQ_FOREACH_SAFE(ds, &dr->dr_res_ptr, ds_ptr_next, ds2)
+		dbr_res_del(ds);
 
 	TAILQ_FOREACH_SAFE(dr2, &dr->dr_clone.head, dr_clone.next, dr_tmp) {
-		/* TODO: clear ouput queue */
-		MDNS_INIT_ASSERT(dr2, dr_magic);
-		r = &dr2->dr_rec;
-		TAILQ_REMOVE(&dr->dr_clone.head, dr2, dr_clone.next);
-		dr->dr_clones--;
-		dprintf(DEBUG_DBR, "Removing record clone dr=%x, orig=%x", dr2, dr);
-		record_foreach_type(r, record_type_del_cb, NULL);
-		record_release(r);
-		assert(record_refcnt(r) == 0);
-		MDNS_INIT_UNSET(dr2, dr_magic);
-		free(dr2);
+		rec_del(dr2);
+	}
+	rec_del(dr);
+	dprintf(DEBUG_DBR, "Removed record dr=%x", dr);
+}
+
+/*
+ * Clone a resource
+ */
+static struct dbr_res *
+clone_res(struct dbr_res *orig)
+{
+	struct dbr_res *ds;
+
+	ds = malloc(sizeof(struct dbr_res));
+	if (ds == NULL)
+		return (NULL);
+	memcpy(ds, orig, sizeof(struct dbr_res));
+	ds->ds_data = NULL;
+	ds->ds_flags |= DS_CLONE | DS_INVALID;
+	ds->ds_time.tv_nsec = ds->ds_time.tv_sec = 0;
+	TAILQ_INSERT_TAIL(&orig->ds_clone.head, ds, ds_clone.next);
+	dprintf(DEBUG_DBR, "Database resource cloned ds=%x, source=%x",
+	    ds, orig);
+	return (ds);
+}
+
+/*
+ * Update a resource with new data
+ */
+static void
+res_update(struct dbr_res *ds, wchar_t **res, size_t reslen)
+{
+	struct dbr_rec *dr;
+	struct dbr_type *dt;
+	struct dbr_res *ds_clone, *ds_tmp;
+	struct record *r;
+	struct record_type *rt;
+	struct record_res *rr;
+	char *p;
+	size_t rlen;	
+	int32_t diff;
+	uint32_t i;
+	
+	MDNS_INIT_ASSERT(ds, ds_magic);
+	dt = ds->ds_type;
+	MDNS_INIT_ASSERT(dt, dt_magic);
+	dr = dt->dt_rec;
+	MDNS_INIT_ASSERT(dr, dr_magic);
+	rt = &dt->dt_type;
+	rr = &ds->ds_res;
+	r = rt->rt_record;
+
+	assert(ds == ds->ds_chead);
+
+	/*
+	 * If there are no resources available in the provided array,
+	 * invalidate the record and remove existing clones.
+	 */
+	if (reslen == 0) {
+		if (ds->ds_data != NULL)
+			free(ds->ds_data);
+		record_res_setdata(&ds->ds_res, NULL, 0);
+		ds->ds_data = NULL;
+		ds->ds_flags |= DS_INVALID;
+		
+		TAILQ_FOREACH_SAFE(ds_clone, &ds->ds_clone.head,
+		    ds_clone.next, ds_tmp) {
+			res_del(ds_clone);
+		}
+		return;
+	}
+
+	i = 1;
+	p = mdns_res_encode(mdns_c_in, rt->rt_type, res[0],
+	    MDNS_ENC_WCHAR, wcslen(res[0]), &rlen);
+	if (ds->ds_data != NULL)
+		free(ds->ds_data);
+	record_res_setdata(&ds->ds_res, p, rlen);
+	ds->ds_flags &= ~DS_INVALID;
+	ds->ds_data = res[0];
+
+	/*
+	 * Replace resource data as long as there is enough clones
+	 * and results available.
+	 */
+	TAILQ_FOREACH(ds_clone, &ds->ds_clone.head, ds_clone.next) {
+		if ((i - 1) == ds->ds_clones || i == reslen)
+			break;
+		if (!(ds_clone->ds_flags & DS_INVALID))
+			free(ds_clone->ds_data);
+		ds_clone->ds_data = res[i];
+		p = mdns_res_encode(mdns_c_in, rt->rt_type, res[i],
+		    MDNS_ENC_WCHAR, wcslen(res[i]), &rlen);
+		record_res_setdata(&ds_clone->ds_res, p, rlen);
+		ds_clone->ds_flags &= ~DS_INVALID;
+		i++;
+	}
+
+	/* More results than clones, expand */
+	if (reslen > i) {
+		diff = reslen - i;
+		r = ds->ds_res.rr_type->rt_record;
+		MDNS_INIT_ASSERT(r, r_magic);
+		while (diff-- > 0) {
+			ds_clone = clone_res(ds);
+			ds_clone->ds_clones = 0;
+			ds->ds_clones++;
+			ds_clone->ds_data = res[i];
+			rr = &ds_clone->ds_res;
+			p = mdns_res_encode(mdns_c_in, rt->rt_type,
+			    res[i], MDNS_ENC_WCHAR, wcslen(res[i]), &rlen);
+			record_res_add(r, &rr, RECORD_NOALLOC, rt->rt_type,
+			    p, rlen);
+			record_res_setparent(&ds_clone->ds_res, ds);
+			i++;
+			ds_clone->ds_flags &= ~DS_INVALID;
+		}
+	}
+	/* More clones than results, shrink */
+	else if (ds->ds_clones >= i) {
+		diff = ds->ds_clones - i;
+		while (diff-- >= 0) {
+			ds_tmp = TAILQ_LAST(&ds->ds_clone.head, clone_head);
+			res_del(ds_tmp);
+		}
 	}
 
-	r = &dr->dr_rec;
-	record_foreach_type(r, record_type_del_cb, NULL);
-	record_release(r);
-	assert(record_refcnt(r) == 0);
-	i = 0;
-	do {
-		wp = dr->dr_names[i++];
-		if (wp != NULL)
-			free(wp);
-	} while(wp != NULL);
-	free(dr->dr_names);
-	MDNS_INIT_UNSET(dr, dr_magic);
-	free(dr);
-	dprintf(DEBUG_DBR, "Removed record dr=%x", dr);
+	dprintf(DEBUG_DBR, "Resource ds=%x updated, clones=%d",
+	    ds, ds->ds_clones);
 }
 
+/*
+ * Variable update handler to resource data, will re-expand vaiables
+ * and update affected resources.
+ * This is an asynchronous callback routine which is called from the
+ * variable event system.
+ *
+ *   ptr - Opaque pointer passed to var_reg(), to this routine it's
+ *         always of type dbr_res {}
+ */
+static void
+res_var_update(void *ptr)
+{
+	struct dbr *dbr;
+	struct dbr_rec *dr;
+	struct dbr_type *dt;
+	struct dbr_res *ds;
+	struct dbr_pac *pac;
+	struct vt_data *vtd;
+	size_t vtd_len;
+	uint32_t i;
+	wchar_t **names;
+
+	ds = ptr;
+	MDNS_INIT_ASSERT(ds, ds_magic);
+	dt = ds->ds_type;
+	MDNS_INIT_ASSERT(dt, dt_magic);
+	dr = dt->dt_rec;
+	MDNS_INIT_ASSERT(dr, dr_magic);
+	dbr = dr->dr_dbr;
+	MDNS_INIT_ASSERT(dbr, dbr_magic);
+
+	/*
+	 * We should never get a "pointer resource" here, they are updated
+	 * when the rrset they point to are updated.
+	 */
+	assert(!(ds->ds_flags & DS_POINTER));
+
+	if (dr->dr_flags & DR_PROBING)
+		dbr_probe_del(dr);
+	if (dr->dr_flags & DR_ANNOUNCE)
+		dbr_an_del(dr);
+
+	vtd = var_expand(&dbr->dbr_vars, ds->ds_vdata.wp, &vtd_len);
+	names = malloc(sizeof(wchar_t *) * vtd_len);
+	for (i = 0; i < vtd_len; i++) {
+		names[i] = vtd[i].vtd_str;
+	}
+	res_update(ds, names, vtd_len);
+	free(vtd);
+
+	if (dr->dr_flags & DR_OK) {
+		pac = dbr_pac_new(dbr, PAC_ANNOUNCE);
+		dbr_an_add(pac, dr);
+		dbr_an_start(pac);
+	}
+	else {
+		pac = dbr_pac_new(dbr, PAC_PROBE);
+		dbr_probe_add(pac, dr);
+		dbr_probe_start(pac);
+	}
+}
+/*
+ * Add a resource to database record
+ *   dr   - Parent database record
+ *   type - DNS type
+ *   ttl  - Resource TTL
+ *   res  - Unexpanded resource string
+ */
 struct dbr_res *
-dbr_res_add(struct dbr_rec *dr, uint16_t type, uint32_t ttl, wchar_t *res)
+dbr_res_add(struct dbr_rec *dr, uint16_t type, uint32_t ttl, void *res, int ptr)
 {
 	struct record *r;
+	struct record_type *rt, *rt2;
 	struct record_res *rr;
-	struct record_type *rt, *rt2;
 	struct dbr *dbr;
-	struct dbr_rec *dr_orig;
-	struct dbr_res *ds, *ds2;
+	struct dbr_rec *dr_orig, *drp, *drp2;
+	struct dbr_res *ds;
 	struct dbr_type *dt;
 	struct vt_data *vtd;
-	size_t vtd_len, i, j, rlen;
-	char *p;
+	size_t vtd_len, j, namlen;
+	wchar_t *wp, **names;
+	char *ident;
 
 	MDNS_INIT_ASSERT(dr, dr_magic);
 	dbr = dr->dr_dbr;
 	MDNS_INIT_ASSERT(dbr, dbr_magic);
 
+	wp = res;
+	ident = res;
+	names = NULL;
+	namlen = 0;
+
+	/*
+	 * If we where passed a pointer, make sure it exists.
+	 */
+	if (ptr) {
+		drp = hashtbl_find(&dbr->dbr_ident, ident, strlen(ident));
+		if (drp == NULL)
+			return (NULL);

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list