diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..037aafc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+SOURCES/open-iscsi-2.0.874.tar.gz
diff --git a/.iscsi-initiator-utils.metadata b/.iscsi-initiator-utils.metadata
new file mode 100644
index 0000000..e6e0a84
--- /dev/null
+++ b/.iscsi-initiator-utils.metadata
@@ -0,0 +1 @@
+803e7b6677ecc81f76bdd280b771a07bbc7df263 SOURCES/open-iscsi-2.0.874.tar.gz
diff --git a/README.md b/README.md
deleted file mode 100644
index 0e7897f..0000000
--- a/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-The master branch has no content
- 
-Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6
- 
-If you find this file in a distro specific branch, it means that no content has been checked in yet
diff --git a/SOURCES/0143-idmb_rec_write-check-for-tpgt-first.patch b/SOURCES/0143-idmb_rec_write-check-for-tpgt-first.patch
new file mode 100644
index 0000000..cdc958a
--- /dev/null
+++ b/SOURCES/0143-idmb_rec_write-check-for-tpgt-first.patch
@@ -0,0 +1,55 @@
+From 24a4d8156786dfd91dcc17b2472653e963ebd028 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Tue, 13 Aug 2013 10:59:44 -0700
+Subject: idmb_rec_write, check for tpgt first
+
+Factor out the check for a tpgt to a single place, before going crazy on
+the rec files.  Makes flow of this function easier to follow, and preps
+for splitting it up.
+---
+ usr/idbm.c | 18 +++++-------------
+ 1 file changed, 5 insertions(+), 13 deletions(-)
+
+diff --git a/usr/idbm.c b/usr/idbm.c
+index 1e4f8c8..0a88699 100644
+--- a/usr/idbm.c
++++ b/usr/idbm.c
+@@ -1849,6 +1849,10 @@ static int idbm_rec_write(node_rec_t *rec)
+ 	if (rc)
+ 		goto free_portal;
+ 
++	if (rec->tpgt == PORTAL_GROUP_TAG_UNKNOWN)
++		/* drop down to old style portal as config */
++		goto open_conf;
++
+ 	rc = stat(portal, &statb);
+ 	if (rc) {
+ 		rc = 0;
+@@ -1857,23 +1861,11 @@ static int idbm_rec_write(node_rec_t *rec)
+ 		 * set the tgpt. In new versions you must pass all the info in
+ 		 * from the start
+ 		 */
+-		if (rec->tpgt == PORTAL_GROUP_TAG_UNKNOWN)
+-			/* drop down to old style portal as config */
+-			goto open_conf;
+-		else
+-			goto mkdir_portal;
++		goto mkdir_portal;
+ 	}
+ 
+ 	if (!S_ISDIR(statb.st_mode)) {
+ 		/*
+-		 * older iscsiadm versions had you create the config then set
+-		 * set the tgpt. In new versions you must pass all the info in
+-		 * from the start
+-		 */
+-		if (rec->tpgt == PORTAL_GROUP_TAG_UNKNOWN)
+-			/* drop down to old style portal as config */
+-			goto open_conf;
+-		/*
+ 		 * Old style portal as a file, but with tpgt. Let's update it.
+ 		 */
+ 		if (unlink(portal)) {
+-- 
+1.8.1.4
+
diff --git a/SOURCES/0145-idbm_rec_write-seperate-old-and-new-style-writes.patch b/SOURCES/0145-idbm_rec_write-seperate-old-and-new-style-writes.patch
new file mode 100644
index 0000000..f8b4dbf
--- /dev/null
+++ b/SOURCES/0145-idbm_rec_write-seperate-old-and-new-style-writes.patch
@@ -0,0 +1,180 @@
+From 220f360b81246a45d6246b85f7b6bf4133f3c213 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Tue, 13 Aug 2013 11:34:31 -0700
+Subject: [PATCH] idbm_rec_write, seperate old and new style writes
+
+Duplicates a small bit of code, but easier to understand and extened.
+---
+ usr/idbm.c | 116 +++++++++++++++++++++++++++++++++++++++++--------------------
+ 1 file changed, 79 insertions(+), 37 deletions(-)
+
+diff --git a/usr/idbm.c b/usr/idbm.c
+index caec94f..2b4f0da 100644
+--- a/usr/idbm.c
++++ b/usr/idbm.c
+@@ -2000,7 +2000,7 @@ mkdir_portal:
+ 	return f;
+ }
+ 
+-static int idbm_rec_write(node_rec_t *rec)
++static int idbm_rec_write_new(node_rec_t *rec)
+ {
+ 	struct stat statb;
+ 	FILE *f;
+@@ -2012,38 +2012,8 @@ static int idbm_rec_write(node_rec_t *rec)
+ 		log_error("Could not alloc portal");
+ 		return ISCSI_ERR_NOMEM;
+ 	}
+-
+-	snprintf(portal, PATH_MAX, "%s", NODE_CONFIG_DIR);
+-	if (access(portal, F_OK) != 0) {
+-		if (mkdir(portal, 0660) != 0) {
+-			log_error("Could not make %s: %s", portal,
+-				  strerror(errno));
+-			rc = ISCSI_ERR_IDBM;
+-			goto free_portal;
+-		}
+-	}
+-
+-	snprintf(portal, PATH_MAX, "%s/%s", NODE_CONFIG_DIR, rec->name);
+-	if (access(portal, F_OK) != 0) {
+-		if (mkdir(portal, 0660) != 0) {
+-			log_error("Could not make %s: %s", portal,
+-				  strerror(errno));
+-			rc = ISCSI_ERR_IDBM;
+-			goto free_portal;
+-		}
+-	}
+-
+ 	snprintf(portal, PATH_MAX, "%s/%s/%s,%d", NODE_CONFIG_DIR,
+ 		 rec->name, rec->conn[0].address, rec->conn[0].port);
+-	log_debug(5, "Looking for config file %s", portal);
+-
+-	rc = idbm_lock();
+-	if (rc)
+-		goto free_portal;
+-
+-	if (rec->tpgt == PORTAL_GROUP_TAG_UNKNOWN)
+-		/* drop down to old style portal as config */
+-		goto open_conf;
+ 
+ 	rc = stat(portal, &statb);
+ 	if (rc) {
+@@ -2064,11 +2034,11 @@ static int idbm_rec_write(node_rec_t *rec)
+ 			log_error("Could not convert %s: %s", portal,
+ 				  strerror(errno));
+ 			rc = ISCSI_ERR_IDBM;
+-			goto unlock;
++			goto free_portal;
+ 		}
+ 	} else {
+ 		rc = ISCSI_ERR_INVAL;
+-		goto unlock;
++		goto free_portal;
+ 	}	
+ 
+ mkdir_portal:
+@@ -2079,24 +2049,96 @@ mkdir_portal:
+ 			log_error("Could not make dir %s: %s",
+ 				  portal, strerror(errno));
+ 			rc = ISCSI_ERR_IDBM;
+-			goto unlock;
++			goto free_portal;
+ 		}
+ 	}
+ 
+ 	snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d/%s", NODE_CONFIG_DIR,
+ 		 rec->name, rec->conn[0].address, rec->conn[0].port, rec->tpgt,
+ 		 rec->iface.name);
+-open_conf:
++/* open_conf: */
+ 	f = fopen(portal, "w");
+ 	if (!f) {
+ 		log_error("Could not open %s: %s", portal, strerror(errno));
+ 		rc = ISCSI_ERR_IDBM;
+-		goto unlock;
++		goto free_portal;
+ 	}
+ 
+ 	idbm_print(IDBM_PRINT_TYPE_NODE, rec, 1, f);
+ 	fclose(f);
+-unlock:
++free_portal:
++	free(portal);
++	return rc;
++}
++
++static int idbm_rec_write_old(node_rec_t *rec)
++{
++	FILE *f;
++	char *portal;
++	int rc = 0;
++
++	portal = malloc(PATH_MAX);
++	if (!portal) {
++		log_error("Could not alloc portal");
++		return ISCSI_ERR_NOMEM;
++	}
++	snprintf(portal, PATH_MAX, "%s/%s/%s,%d", NODE_CONFIG_DIR,
++		 rec->name, rec->conn[0].address, rec->conn[0].port);
++
++	f = fopen(portal, "w");
++	if (!f) {
++		log_error("Could not open %s: %s", portal, strerror(errno));
++		rc = ISCSI_ERR_IDBM;
++		goto free_portal;
++	}
++	idbm_print(IDBM_PRINT_TYPE_NODE, rec, 1, f);
++	fclose(f);
++free_portal:
++	free(portal);
++	return rc;
++}
++
++static int idbm_rec_write(node_rec_t *rec)
++{
++	char *portal;
++	int rc = 0;
++
++	portal = malloc(PATH_MAX);
++	if (!portal) {
++		log_error("Could not alloc portal");
++		return ISCSI_ERR_NOMEM;
++	}
++
++	snprintf(portal, PATH_MAX, "%s", NODE_CONFIG_DIR);
++	if (access(portal, F_OK) != 0) {
++		if (mkdir(portal, 0660) != 0) {
++			log_error("Could not make %s: %s", portal,
++				  strerror(errno));
++			rc = ISCSI_ERR_IDBM;
++			goto free_portal;
++		}
++	}
++
++	snprintf(portal, PATH_MAX, "%s/%s", NODE_CONFIG_DIR, rec->name);
++	if (access(portal, F_OK) != 0) {
++		if (mkdir(portal, 0660) != 0) {
++			log_error("Could not make %s: %s", portal,
++				  strerror(errno));
++			rc = ISCSI_ERR_IDBM;
++			goto free_portal;
++		}
++	}
++
++	rc = idbm_lock();
++	if (rc)
++		goto free_portal;
++
++	if (rec->tpgt == PORTAL_GROUP_TAG_UNKNOWN)
++		/* old style portal as config */
++		rc = idbm_rec_write_old(rec);
++	else
++		rc = idbm_rec_write_new(rec);
++
+ 	idbm_unlock();
+ free_portal:
+ 	free(portal);
+-- 
+2.5.5
+
diff --git a/SOURCES/0146-idbw_rec_write-pick-tpgt-from-existing-record.patch b/SOURCES/0146-idbw_rec_write-pick-tpgt-from-existing-record.patch
new file mode 100644
index 0000000..28785da
--- /dev/null
+++ b/SOURCES/0146-idbw_rec_write-pick-tpgt-from-existing-record.patch
@@ -0,0 +1,87 @@
+From 69de52494cd8f57762ca9b0b7811929f7f9b4287 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Tue, 13 Aug 2013 12:39:07 -0700
+Subject: [PATCH] idbw_rec_write, pick tpgt from existing record
+
+On a static add (-m node -o new) without a user specified tpgt, looks
+for existing new style records with tpgt before creating an old style
+record without.  If one exists, take the tpgt from it an write an
+updated new style record instead.
+---
+ usr/idbm.c | 40 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+
+diff --git a/usr/idbm.c b/usr/idbm.c
+index 2b4f0da..a2ad57f 100644
+--- a/usr/idbm.c
++++ b/usr/idbm.c
+@@ -27,6 +27,7 @@
+ #include <errno.h>
+ #include <dirent.h>
+ #include <limits.h>
++#include <glob.h>
+ #include <sys/stat.h>
+ #include <sys/file.h>
+ 
+@@ -162,6 +163,8 @@ static struct idbm *db;
+ 	_n++; \
+ } while(0)
+ 
++static int idbm_remove_disc_to_node_link(node_rec_t *rec, char *portal);
++
+ static void
+ idbm_recinfo_discovery(discovery_rec_t *r, recinfo_t *ri)
+ {
+@@ -2076,12 +2079,49 @@ static int idbm_rec_write_old(node_rec_t *rec)
+ 	FILE *f;
+ 	char *portal;
+ 	int rc = 0;
++	glob_t globbuf;
++	int i;
++	int tpgt = PORTAL_GROUP_TAG_UNKNOWN;
+ 
+ 	portal = malloc(PATH_MAX);
+ 	if (!portal) {
+ 		log_error("Could not alloc portal");
+ 		return ISCSI_ERR_NOMEM;
+ 	}
++
++	/* check for newer portal dir with tpgt */
++	snprintf(portal, PATH_MAX, "%s/%s/%s,%d,*", NODE_CONFIG_DIR,
++		 rec->name, rec->conn[0].address, rec->conn[0].port);
++	rc = glob(portal, GLOB_ONLYDIR, NULL, &globbuf);
++	if (!rc) {
++		if (globbuf.gl_pathc > 1)
++			log_warning("multiple tpg records for portal "
++				    "%s/%s:%d found", rec->name,
++				    rec->conn[0].address, rec->conn[0].port);
++		/* set pattern for sscanf matching of tpgt */
++		snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%%u", NODE_CONFIG_DIR,
++			 rec->name, rec->conn[0].address, rec->conn[0].port);
++		for (i = 0; i < globbuf.gl_pathc; i++) {
++			rc = sscanf(globbuf.gl_pathv[i], portal, &tpgt);
++			if (rc == 1)
++				break;
++		}
++		if (tpgt == PORTAL_GROUP_TAG_UNKNOWN)
++			log_warning("glob match on existing records, "
++				    "but no valid tpgt found");
++	}
++	globfree(&globbuf);
++	rc = 0;
++
++	/* if a tpgt was selected from an old record, write entry in new format */
++	if (tpgt != PORTAL_GROUP_TAG_UNKNOWN) {
++		log_warning("using tpgt %u from existing record", tpgt);
++		rec->tpgt = tpgt;
++		rc = idbm_remove_disc_to_node_link(rec, portal);
++		free(portal);
++		return idbm_rec_write_new(rec);
++	}
++
+ 	snprintf(portal, PATH_MAX, "%s/%s/%s,%d", NODE_CONFIG_DIR,
+ 		 rec->name, rec->conn[0].address, rec->conn[0].port);
+ 
+-- 
+2.5.5
+
diff --git a/SOURCES/0149-update-systemd-service-files-add-iscsi.service-for-s.patch b/SOURCES/0149-update-systemd-service-files-add-iscsi.service-for-s.patch
new file mode 100644
index 0000000..966bb9f
--- /dev/null
+++ b/SOURCES/0149-update-systemd-service-files-add-iscsi.service-for-s.patch
@@ -0,0 +1,93 @@
+From 1c3b1d23e0b3f17399ffd4463cafad813b0444d5 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Wed, 19 Dec 2012 15:07:36 -0800
+Subject: update systemd service files, add iscsi.service for starting
+ sessions on boot
+
+Signed-off-by: Chris Leech <cleech@redhat.com>
+---
+ etc/systemd/iscsi.service         | 19 +++++++++++++++++++
+ etc/systemd/iscsi_mark_root_nodes | 14 ++++++++++++++
+ etc/systemd/iscsid.service        |  7 +++++--
+ etc/systemd/iscsid.socket         |  2 +-
+ 4 files changed, 39 insertions(+), 3 deletions(-)
+ create mode 100644 etc/systemd/iscsi.service
+ create mode 100755 etc/systemd/iscsi_mark_root_nodes
+
+diff --git a/etc/systemd/iscsi.service b/etc/systemd/iscsi.service
+new file mode 100644
+index 0000000..bbd52fd
+--- /dev/null
++++ b/etc/systemd/iscsi.service
+@@ -0,0 +1,19 @@
++[Unit]
++Description=Login and scanning of iSCSI devices
++Documentation=man:iscsid(8) man:iscsiadm(8)
++DefaultDependencies=no
++Conflicts=shutdown.target
++After=systemd-remount-fs.service network.target iscsid.service iscsiuio.service
++Before=remote-fs-pre.target
++ConditionPathExists=/etc/iscsi/initiatorname.iscsi
++
++[Service]
++Type=oneshot
++RemainAfterExit=true
++ExecStart=/usr/libexec/iscsi_mark_root_nodes
++ExecStart=/sbin/iscsiadm -m node --loginall=automatic
++ExecStop=/bin/sync
++ExecStop=/sbin/iscsiadm -m node --logoutall=automatic
++
++[Install]
++WantedBy=sysinit.target
+diff --git a/etc/systemd/iscsi_mark_root_nodes b/etc/systemd/iscsi_mark_root_nodes
+new file mode 100755
+index 0000000..c68475c
+--- /dev/null
++++ b/etc/systemd/iscsi_mark_root_nodes
+@@ -0,0 +1,14 @@
++#!/bin/bash
++
++ISCSIADM=/sbin/iscsiadm
++SESSION_FILE=/run/initramfs/iscsi.sessions
++
++if [ ! -f $SESSION_FILE ] ; then
++  exit 0
++fi
++
++while read t num i target; do
++  ip=${i%:*}
++  $ISCSIADM -m node -p $ip -T $target -o update -n node.startup -v onboot
++done < $SESSION_FILE
++
+diff --git a/etc/systemd/iscsid.service b/etc/systemd/iscsid.service
+index 028e0b3..653dd08 100644
+--- a/etc/systemd/iscsid.service
++++ b/etc/systemd/iscsid.service
+@@ -1,7 +1,10 @@
+ [Unit]
+ Description=Open-iSCSI
+-Documentation=man:iscsid(8) man:iscsiuio(8) man:iscsiadm(8)
+-After=network.target NetworkManager-wait-online.service iscsiuio.service tgtd.service targetcli.service
++Documentation=man:iscsid(8) man:iscsiadm(8)
++DefaultDependencies=no
++Conflicts=shutdown.target
++After=network.target iscsiuio.service
++Before=remote-fs-pre.target
+ 
+ [Service]
+ Type=forking
+diff --git a/etc/systemd/iscsid.socket b/etc/systemd/iscsid.socket
+index 832451d..58a8d12 100644
+--- a/etc/systemd/iscsid.socket
++++ b/etc/systemd/iscsid.socket
+@@ -1,6 +1,6 @@
+ [Unit]
+ Description=Open-iSCSI iscsid Socket
+-Documentation=man:iscsid(8) man:iscsiuio(8) man:iscsiadm(8)
++Documentation=man:iscsid(8) man:iscsiadm(8)
+ 
+ [Socket]
+ ListenStream=@ISCSIADM_ABSTRACT_NAMESPACE
+-- 
+1.7.11.7
+
diff --git a/SOURCES/0150-iscsi-boot-related-service-file-updates.patch b/SOURCES/0150-iscsi-boot-related-service-file-updates.patch
new file mode 100644
index 0000000..19450ee
--- /dev/null
+++ b/SOURCES/0150-iscsi-boot-related-service-file-updates.patch
@@ -0,0 +1,75 @@
+From 8f79529354b4023c371e00091f11bdd523497639 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Mon, 19 Aug 2013 07:18:25 -0700
+Subject: iscsi boot related service file updates
+
+make sure iscsid gets started if there are any boot sessions running
+add reload target to fix double session problem when restarting from NM
+don't rely on session list passed from initrd, never got fully implemented
+---
+ etc/systemd/iscsi-mark-root-nodes | 13 +++++++++++++
+ etc/systemd/iscsi.service         |  3 ++-
+ etc/systemd/iscsi_mark_root_nodes | 14 --------------
+ 3 files changed, 15 insertions(+), 15 deletions(-)
+ create mode 100644 etc/systemd/iscsi-mark-root-nodes
+ delete mode 100644 etc/systemd/iscsi_mark_root_nodes
+
+diff --git a/etc/systemd/iscsi-mark-root-nodes b/etc/systemd/iscsi-mark-root-nodes
+new file mode 100644
+index 0000000..157be62
+--- /dev/null
++++ b/etc/systemd/iscsi-mark-root-nodes
+@@ -0,0 +1,13 @@
++#!/bin/bash
++
++ISCSIADM=/sbin/iscsiadm
++
++$ISCSIADM -m session >/dev/null 2>&1 || exit 0
++
++$ISCSIADM -m session | while read t num i target; do
++  ip=${i%:*}
++  $ISCSIADM -m node -p $ip -T $target -o update -n node.startup -v onboot
++done
++
++systemctl start iscsid.service
++
+diff --git a/etc/systemd/iscsi.service b/etc/systemd/iscsi.service
+index 7b4efee..d5712bd 100644
+--- a/etc/systemd/iscsi.service
++++ b/etc/systemd/iscsi.service
+@@ -10,10 +10,11 @@ ConditionDirectoryNotEmpty=/var/lib/iscsi/nodes
+ [Service]
+ Type=oneshot
+ RemainAfterExit=true
+-ExecStart=/usr/libexec/iscsi_mark_root_nodes
++ExecStart=/usr/libexec/iscsi-mark-root-nodes
+ ExecStart=/sbin/iscsiadm -m node --loginall=automatic
+ ExecStop=/bin/sync
+ ExecStop=/sbin/iscsiadm -m node --logoutall=automatic
++ExecReload=/sbin/iscsiadm -m node --loginall=automatic
+ 
+ [Install]
+ WantedBy=sysinit.target
+diff --git a/etc/systemd/iscsi_mark_root_nodes b/etc/systemd/iscsi_mark_root_nodes
+deleted file mode 100644
+index c68475c..0000000
+--- a/etc/systemd/iscsi_mark_root_nodes
++++ /dev/null
+@@ -1,14 +0,0 @@
+-#!/bin/bash
+-
+-ISCSIADM=/sbin/iscsiadm
+-SESSION_FILE=/run/initramfs/iscsi.sessions
+-
+-if [ ! -f $SESSION_FILE ] ; then
+-  exit 0
+-fi
+-
+-while read t num i target; do
+-  ip=${i%:*}
+-  $ISCSIADM -m node -p $ip -T $target -o update -n node.startup -v onboot
+-done < $SESSION_FILE
+-
+-- 
+1.8.1.4
+
diff --git a/SOURCES/0151-update-initscripts-and-docs.patch b/SOURCES/0151-update-initscripts-and-docs.patch
new file mode 100644
index 0000000..c7cc8a9
--- /dev/null
+++ b/SOURCES/0151-update-initscripts-and-docs.patch
@@ -0,0 +1,130 @@
+From 714a9dbed8e4c9d943ce34896a58d48a174f54cb Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Mon, 19 Nov 2012 16:37:13 -0800
+Subject: [PATCH] update initscripts and docs
+
+---
+ README          |  9 +++------
+ etc/iscsid.conf | 23 +++++++++++------------
+ usr/idbm.c      |  4 ++++
+ 3 files changed, 18 insertions(+), 18 deletions(-)
+
+diff --git a/README b/README
+index cbe8763..8db3013 100644
+--- a/README
++++ b/README
+@@ -74,11 +74,6 @@ the cache sync command will fail.
+ - iscsiadm's -P 3 option will not print out scsi devices.
+ - iscsid will not automatically online devices.
+ 
+-You need to enable "Cryptographic API" under "Cryptographic options" in the
+-kernel config. And you must enable "CRC32c CRC algorithm" even if
+-you do not use header or data digests. They are the kernel options,
+-CONFIG_CRYPTO and CONFIG_CRYPTO_CRC32C, respectively.
+-
+ The userspace components: iscsid, iscsiadm and iscsistart require the
+ open-isns library which can be found here:
+ 
+@@ -1132,7 +1127,7 @@ Red Hat or Fedora:
+ -----------------
+ To start open-iscsi in Red Hat/Fedora you can do:
+ 
+-	service open-iscsi start
++	service iscsi start
+ 
+ To get open-iscsi to automatically start at run time you may have to
+ run:
+@@ -1340,6 +1335,8 @@ iscsid will only perform rediscovery when it gets a SCN from the server.
+ #   linux-isns (SLES's iSNS server) where it sometimes does not send SCN
+ #   events in the proper format, so they may not get handled.
+ 
++To set the startup value, so that nodes are not logged into automatically
++use the value "manual".
+ 
+ Example:
+ --------
+diff --git a/etc/iscsid.conf b/etc/iscsid.conf
+index c30a7dc..cfa6844 100644
+--- a/etc/iscsid.conf
++++ b/etc/iscsid.conf
+@@ -17,10 +17,10 @@
+ # maintainers.
+ #
+ # Default for Fedora and RHEL. (uncomment to activate).
+-# iscsid.startup = /etc/rc.d/init.d/iscsid force-start
++iscsid.startup = /etc/rc.d/init.d/iscsid force-start
+ # 
+ # Default for upstream open-iscsi scripts (uncomment to activate).
+-iscsid.startup = /sbin/iscsid
++# iscsid.startup = /sbin/iscsid
+ 
+ # Check for active mounts on devices reachable through a session
+ # and refuse to logout if there are any.  Defaults to "No".
+@@ -39,8 +39,8 @@ iscsid.startup = /sbin/iscsid
+ # To request that the iscsi initd scripts startup a session set to "automatic".
+ # node.startup = automatic
+ #
+-# To manually startup the session set to "manual". The default is manual.
+-node.startup = manual
++# To manually startup the session set to "manual". The default is automatic.
++node.startup = automatic
+ 
+ # For "automatic" startup nodes, setting this to "Yes" will try logins on each
+ # available iface until one succeeds, and then stop.  The default "No" will try
+@@ -262,28 +262,27 @@ node.conn[0].iscsi.MaxXmitDataSegmentLength = 0
+ discovery.sendtargets.iscsi.MaxRecvDataSegmentLength = 32768
+ 
+ # To allow the targets to control the setting of the digest checking,
+-# with the initiator requesting a preference of enabling the checking, uncomment# one or both of the following lines:
++# with the initiator requesting a preference of enabling the checking, uncomment
++# the following lines (Data digests are not supported.):
+ #node.conn[0].iscsi.HeaderDigest = CRC32C,None
+-#node.conn[0].iscsi.DataDigest = CRC32C,None
++
+ #
+ # To allow the targets to control the setting of the digest checking,
+ # with the initiator requesting a preference of disabling the checking,
+-# uncomment one or both of the following lines:
++# uncomment the following line:
+ #node.conn[0].iscsi.HeaderDigest = None,CRC32C
+-#node.conn[0].iscsi.DataDigest = None,CRC32C
+ #
+ # To enable CRC32C digest checking for the header and/or data part of
+-# iSCSI PDUs, uncomment one or both of the following lines:
++# iSCSI PDUs, uncomment the following line:
+ #node.conn[0].iscsi.HeaderDigest = CRC32C
+-#node.conn[0].iscsi.DataDigest = CRC32C
+ #
+ # To disable digest checking for the header and/or data part of
+-# iSCSI PDUs, uncomment one or both of the following lines:
++# iSCSI PDUs, uncomment the following line:
+ #node.conn[0].iscsi.HeaderDigest = None
+-#node.conn[0].iscsi.DataDigest = None
+ #
+ # The default is to never use DataDigests or HeaderDigests.
+ #
++node.conn[0].iscsi.HeaderDigest = None
+ 
+ # For multipath configurations, you may want more than one session to be
+ # created on each iface record.  If node.session.nr_sessions is greater
+diff --git a/usr/idbm.c b/usr/idbm.c
+index 198a5ef..2d64172 100644
+--- a/usr/idbm.c
++++ b/usr/idbm.c
+@@ -509,9 +509,13 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri)
+ 				 IDBM_SHOW, "None", "CRC32C", "CRC32C,None",
+ 				 "None,CRC32C", num, 1);
+ 		sprintf(key, CONN_DATA_DIGEST, i);
++
++#if 0
++We do not support data digests
+ 		__recinfo_int_o4(key, ri, r, conn[i].iscsi.DataDigest, IDBM_SHOW,
+ 				 "None", "CRC32C", "CRC32C,None",
+ 				 "None,CRC32C", num, 1);
++#endif
+ 		sprintf(key, CONN_IFMARKER, i);
+ 		__recinfo_int_o2(key, ri, r, conn[i].iscsi.IFMarker, IDBM_SHOW,
+ 				"No", "Yes", num, 1);
+-- 
+2.5.5
+
diff --git a/SOURCES/0152-use-var-for-config.patch b/SOURCES/0152-use-var-for-config.patch
new file mode 100644
index 0000000..57baebb
--- /dev/null
+++ b/SOURCES/0152-use-var-for-config.patch
@@ -0,0 +1,239 @@
+From c229e7d74a3103814403f7c242abcbaef211e259 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Mon, 19 Nov 2012 16:38:45 -0800
+Subject: use var for config
+
+---
+ README         | 33 ++++++++++++++++-----------------
+ doc/iscsiadm.8 |  8 ++++----
+ usr/idbm.c     |  6 +++---
+ usr/idbm.h     | 13 +++++++------
+ usr/iface.h    |  4 +++-
+ 5 files changed, 33 insertions(+), 31 deletions(-)
+
+diff --git a/README b/README
+index 3757b2d26d0d..fa38c8c82dcf 100644
+--- a/README
++++ b/README
+@@ -170,8 +170,7 @@ Usage: iscsid [OPTION]
+ 
+ Open-iSCSI persistent configuration is stored in a number of
+ directories under a configuration root directory, using a flat-file
+-format. This configuration root directory is /etc/iscsi by default,
+-but may also commonly be in /var/lib/iscsi.
++format. This configuration root directory is /var/lib/iscsi by default.
+ 
+ Configuration is contained in directories for:
+ 
+@@ -489,7 +488,7 @@ a scsi_host per HBA port).
+ To manage both types of initiator stacks, iscsiadm uses the interface (iface)
+ structure. For each HBA port or for software iscsi for each network
+ device (ethX) or NIC, that you wish to bind sessions to you must create
+-a iface config /etc/iscsi/ifaces.
++a iface config /var/lib/iscsi/ifaces.
+ 
+ Prep:
+ 
+@@ -523,29 +522,29 @@ Running:
+ iface0 qla4xxx,00:c0:dd:08:63:e8,20.15.0.7,default,iqn.2005-06.com.redhat:madmax
+ iface1 qla4xxx,00:c0:dd:08:63:ea,20.15.0.9,default,iqn.2005-06.com.redhat:madmax
+ 
+-Will report iface configurations that are setup in /etc/iscsi/ifaces.
++Will report iface configurations that are setup in /var/lib/iscsi/ifaces.
+ The format is:
+ 
+ iface_name transport_name,hwaddress,ipaddress,net_ifacename,initiatorname
+ 
+ For software iscsi, you can create the iface configs by hand, but it is
+ recommended that you use iscsiadm's iface mode. There is an iface.example in
+-/etc/iscsi/ifaces which can be used as a template for the daring.
++/var/lib/iscsi/ifaces which can be used as a template for the daring.
+ 
+ For each network object you wish to bind a session to you must create
+-a separate iface config in /etc/iscsi/ifaces and each iface config file
++a separate iface config in /var/lib/iscsi/ifaces and each iface config file
+ must have a unique name which is less than or equal to 64 characters.
+ 
+ Example:
+ 
+ If you have NIC1 with MAC address 00:0F:1F:92:6B:BF and NIC2 with
+ MAC address 00:C0:DD:08:63:E7 and you wanted to do software iscsi over
+-TCP/IP. Then in /etc/iscsi/ifaces/iface0 you would enter:
++TCP/IP. Then in /var/lib/iscsi/ifaces/iface0 you would enter:
+ 
+ iface.transport_name = tcp
+ iface.hwaddress = 00:0F:1F:92:6B:BF
+ 
+-and in /etc/iscsi/ifaces/iface1 you would enter:
++and in /var/lib/iscsi/ifaces/iface1 you would enter:
+ 
+ iface.transport_name = tcp
+ iface.hwaddress = 00:C0:DD:08:63:E7
+@@ -595,7 +594,7 @@ cxgb3i.00:07:43:05:97:07 cxgb3i,00:07:43:05:97:07,<empty>,<empty>,<empty>
+ qla4xxx.00:0e:1e:04:8b:2e qla4xxx,00:0e:1e:04:8b:2e,<empty>,<empty>,<empty>
+ 
+ 
+-Will report iface configurations that are setup in /etc/iscsi/ifaces.
++Will report iface configurations that are setup in /var/lib/iscsi/ifaces.
+ The format is:
+ 
+ iface_name transport_name,hwaddress,ipaddress,net_ifacename,initiatorname
+@@ -681,7 +680,7 @@ need a separate network connection to the target for discovery purposes.
+ *This will be fixed in the next version of open-iscsi*
+ 
+ For compatibility reasons, when you run iscsiadm to do discovery, it
+-will check for interfaces in /etc/iscsi/iscsi/ifaces that are using
++will check for interfaces in /var/lib/iscsi/ifaces that are using
+ tcp for the iface.transport and it will bind the portals that are discovered
+ so that they will be logged in through those ifaces. This behavior can also
+ be overriden by passing in the interfaces you want to use. For the case
+@@ -699,7 +698,7 @@ we do not bind a session to an iface, then you can use the special iface
+ 
+ iscsiadm -m discoverydb -t st -p ip:port -I default --discover -P 1
+ 
+-And if you did not define any interfaces in /etc/iscsi/ifaces and do
++And if you did not define any interfaces in /var/lib/iscsi/ifaces and do
+ not pass anything into iscsiadm, running iscsiadm will do the default
+ behavior, where we allow the network subsystem to decide which
+ device to use.
+@@ -741,7 +740,7 @@ To now log into targets it is the same as with software iscsi. See section
+ 
+ 	    ./iscsiadm -m discoverydb -t st -p 192.168.1.1:3260 --discover
+ 
+-	This will search /etc/iscsi/send_targets for a record with the
++	This will search /var/lib/iscsi/send_targets for a record with the
+ 	ID [portal = 192.168.1.1:3260 and type = sendtargets. If found it
+ 	will perform discovery using the settings stored in the record.
+ 	If a record does not exist, it will be created using the iscsid.conf
+@@ -750,7 +749,7 @@ To now log into targets it is the same as with software iscsi. See section
+ 	The argument to -p may also be a hostname instead of an address.
+ 	    ./iscsiadm -m discoverydb -t st -p smoehost --discover
+ 
+-	For the ifaces, iscsiadm will first search /etc/iscsi/ifaces for
++	For the ifaces, iscsiadm will first search /var/lib/iscsi/ifaces for
+ 	interfaces using software iscsi. If any are found then nodes found
+ 	during discovery will be setup so that they can logged in through
+ 	those interfaces. To specify a specific iface, pass the
+@@ -806,7 +805,7 @@ To now log into targets it is the same as with software iscsi. See section
+ 	This command will perform discovery, but not manipulate the node DB.
+ 
+   - SendTargets iSCSI Discovery with a specific interface. If you
+-	wish to only use a subset of the interfaces in /etc/iscsi/ifaces
++	wish to only use a subset of the interfaces in /var/lib/iscsi/ifaces
+ 	then you can pass them in during discovery:
+ 
+ 	     ./iscsiadm -m discoverydb -t sendtargets -p 192.168.1.1:3260 \
+@@ -1207,8 +1206,8 @@ where targetname is the name of the target and ip_address:port is the address
+ and port of the portal. tpgt, is the portal group tag of
+ the portal, and is not used in iscsiadm commands except for static
+ record creation. And iface name is the name of the iscsi interface
+-defined in /etc/iscsi/ifaces. If no interface was defined in
+-/etc/iscsi/ifaces or passed in, the default behavior is used.
++defined in /var/lib/iscsi/ifaces. If no interface was defined in
++/var/lib/iscsi/ifaces or passed in, the default behavior is used.
+ Default here is iscsi_tcp/tcp to be used over which ever NIC the
+ network layer decides is best.
+ 
+@@ -1323,7 +1322,7 @@ If set, iscsid will perform discovery to the address every
+ discovery.isns.discoveryd_poll_inval or
+ discovery.sendtargets.discoveryd_poll_inval seconds,
+ and it will log into any portals found from the discovery source using
+-the ifaces in /etc/iscsi/ifaces.
++the ifaces in /var/lib/iscsi/ifaces.
+ 
+ Note that for iSNS the poll_interval does not have to be set. If not set,
+ iscsid will only perform rediscovery when it gets a SCN from the server.
+diff --git a/doc/iscsiadm.8 b/doc/iscsiadm.8
+index a82805e28fb9..758a47c2d1fe 100644
+--- a/doc/iscsiadm.8
++++ b/doc/iscsiadm.8
+@@ -241,7 +241,7 @@ This option is only valid for ping submode.
+ .TP
+ \fB\-I\fR, \fB\-\-interface=\fI[iface]\fR
+ The interface argument specifies the iSCSI interface to use for the operation.
+-iSCSI interfaces (iface) are defined in /etc/iscsi/ifaces. For hardware
++iSCSI interfaces (iface) are defined in /var/lib/iscsi/ifaces. For hardware
+ iSCSI (qla4xxx) the iface config must have the hardware address
+ (iface.hwaddress = port's MAC address)
+ and the driver/transport_name (iface.transport_name). The iface's name is
+@@ -318,7 +318,7 @@ If no other options are specified: for \fIdiscovery\fR, \fIdiscoverydb\fR and
+ \fInode\fR, all of their respective records are displayed; for \fIsession\fR,
+ all active sessions and connections are displayed; for \fIfw\fR, all boot
+ firmware values are displayed; for \fIhost\fR, all iSCSI hosts are displayed;
+-and for \fIiface\fR, all ifaces setup in /etc/iscsi/ifaces are displayed.
++and for \fIiface\fR, all ifaces setup in /var/lib/iscsi/ifaces are displayed.
+ 
+ .TP
+ \fB\-n\fR, \fB\-\-name=\fIname\fR
+@@ -703,10 +703,10 @@ The configuration file read by \fBiscsid\fR and \fBiscsiadm\fR on startup.
+ The file containing the iSCSI InitiatorName and InitiatorAlias read by
+ \fBiscsid\fR and \fBiscsiadm\fR on startup.
+ .TP
+-/etc/iscsi/nodes/
++/var/lib/iscsi/nodes/
+ This directory contains the nodes with their targets.
+ .TP
+-/etc/iscsi/send_targets
++/var/lib/iscsi/send_targets
+ This directory contains the portals.
+ 
+ .SH "SEE ALSO"
+diff --git a/usr/idbm.c b/usr/idbm.c
+index 2fe424fb66ba..a749cc8ea3f8 100644
+--- a/usr/idbm.c
++++ b/usr/idbm.c
+@@ -2917,9 +2917,9 @@ free_info:
+ int idbm_init(idbm_get_config_file_fn *fn)
+ {
+ 	/* make sure root db dir is there */
+-	if (access(ISCSI_CONFIG_ROOT, F_OK) != 0) {
+-		if (mkdir(ISCSI_CONFIG_ROOT, 0660) != 0) {
+-			log_error("Could not make %s %d", ISCSI_CONFIG_ROOT,
++	if (access(ISCSIVAR, F_OK) != 0) {
++		if (mkdir(ISCSIVAR, 0660) != 0) {
++			log_error("Could not make %s %d", ISCSIVAR,
+ 				   errno);
+ 			return errno;
+ 		}
+diff --git a/usr/idbm.h b/usr/idbm.h
+index b9020fe4fd0a..b89ddffe55df 100644
+--- a/usr/idbm.h
++++ b/usr/idbm.h
+@@ -29,12 +29,13 @@
+ #include "list.h"
+ #include "flashnode.h"
+ 
+-#define NODE_CONFIG_DIR		ISCSI_CONFIG_ROOT"nodes"
+-#define SLP_CONFIG_DIR		ISCSI_CONFIG_ROOT"slp"
+-#define ISNS_CONFIG_DIR		ISCSI_CONFIG_ROOT"isns"
+-#define STATIC_CONFIG_DIR	ISCSI_CONFIG_ROOT"static"
+-#define FW_CONFIG_DIR		ISCSI_CONFIG_ROOT"fw"
+-#define ST_CONFIG_DIR		ISCSI_CONFIG_ROOT"send_targets"
++#define ISCSIVAR		"/var/lib/iscsi/"
++#define NODE_CONFIG_DIR		ISCSIVAR"nodes"
++#define SLP_CONFIG_DIR		ISCSIVAR"slp"
++#define ISNS_CONFIG_DIR		ISCSIVAR"isns"
++#define STATIC_CONFIG_DIR	ISCSIVAR"static"
++#define FW_CONFIG_DIR		ISCSIVAR"fw"
++#define ST_CONFIG_DIR		ISCSIVAR"send_targets"
+ #define ST_CONFIG_NAME		"st_config"
+ #define ISNS_CONFIG_NAME	"isns_config"
+ 
+diff --git a/usr/iface.h b/usr/iface.h
+index 01f70747dadd..f396918ccc15 100644
+--- a/usr/iface.h
++++ b/usr/iface.h
+@@ -20,7 +20,9 @@
+ #ifndef ISCSI_IFACE_H
+ #define ISCSI_IFACE_H
+ 
+-#define IFACE_CONFIG_DIR	ISCSI_CONFIG_ROOT"ifaces"
++#include "idbm.h"
++
++#define IFACE_CONFIG_DIR	ISCSIVAR"ifaces"
+ 
+ struct iface_rec;
+ struct list_head;
+-- 
+2.9.3
+
diff --git a/SOURCES/0153-use-red-hat-for-name.patch b/SOURCES/0153-use-red-hat-for-name.patch
new file mode 100644
index 0000000..27da7b1
--- /dev/null
+++ b/SOURCES/0153-use-red-hat-for-name.patch
@@ -0,0 +1,39 @@
+From 7f12a1ca8fe699958903278d010cf22d0a98767b Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Mon, 19 Nov 2012 16:40:04 -0800
+Subject: use red hat for name
+
+---
+ doc/iscsi-iname.8   | 2 +-
+ utils/iscsi-iname.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/doc/iscsi-iname.8 b/doc/iscsi-iname.8
+index a55d666d1af3..dd77ed9f3165 100644
+--- a/doc/iscsi-iname.8
++++ b/doc/iscsi-iname.8
+@@ -14,7 +14,7 @@ generates a unique iSCSI node name on every invocation.
+ Display help
+ .TP
+ .BI [-p=]\fIprefix\fP
+-Use the prefix passed in instead of the default "iqn.2005-03.org.open-iscsi"
++Use the prefix passed in instead of the default "iqn.1994-05.com.redhat"
+ 
+ .SH AUTHORS
+ Open-iSCSI project <http://www.open-iscsi.com/>
+diff --git a/utils/iscsi-iname.c b/utils/iscsi-iname.c
+index 6347edc46293..cb2f6c8b8651 100644
+--- a/utils/iscsi-iname.c
++++ b/utils/iscsi-iname.c
+@@ -73,7 +73,7 @@ main(int argc, char *argv[])
+ 			exit(0);
+ 		}
+ 	} else {
+-		prefix = "iqn.2005-03.org.open-iscsi";
++		prefix = "iqn.1994-05.com.redhat";
+ 	}
+ 
+ 	/* try to feed some entropy from the pool to MD5 in order to get
+-- 
+2.9.5
+
diff --git a/SOURCES/0154-add-libiscsi.patch b/SOURCES/0154-add-libiscsi.patch
new file mode 100644
index 0000000..c8737ec
--- /dev/null
+++ b/SOURCES/0154-add-libiscsi.patch
@@ -0,0 +1,3928 @@
+From 2b8ba79bfa079177962465c215d5ea2fccfda400 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Mon, 19 Nov 2012 16:43:15 -0800
+Subject: add libiscsi
+
+---
+ Makefile                                    |    2 +
+ libiscsi/Makefile                           |   61 ++
+ libiscsi/libiscsi.c                         |  620 +++++++++++
+ libiscsi/libiscsi.doxy                      | 1473 +++++++++++++++++++++++++++
+ libiscsi/libiscsi.h                         |  344 +++++++
+ libiscsi/pylibiscsi.c                       |  638 ++++++++++++
+ libiscsi/setup.py                           |    9 +
+ libiscsi/tests/test_discovery_firmware.c    |   53 +
+ libiscsi/tests/test_discovery_sendtargets.c |   60 ++
+ libiscsi/tests/test_get_auth.c              |   70 ++
+ libiscsi/tests/test_get_initiator_name.c    |   38 +
+ libiscsi/tests/test_get_network_config.c    |   45 +
+ libiscsi/tests/test_login.c                 |   52 +
+ libiscsi/tests/test_logout.c                |   51 +
+ libiscsi/tests/test_params.c                |  103 ++
+ libiscsi/tests/test_set_auth.c              |   58 ++
+ usr/Makefile                                |    2 +-
+ usr/discovery.c                             |    5 +
+ usr/idbm.c                                  |    6 +-
+ usr/idbm.h                                  |    3 +
+ usr/iscsi_ipc.h                             |    2 +
+ 21 files changed, 3691 insertions(+), 4 deletions(-)
+ create mode 100644 libiscsi/Makefile
+ create mode 100644 libiscsi/libiscsi.c
+ create mode 100644 libiscsi/libiscsi.doxy
+ create mode 100644 libiscsi/libiscsi.h
+ create mode 100644 libiscsi/pylibiscsi.c
+ create mode 100644 libiscsi/setup.py
+ create mode 100644 libiscsi/tests/test_discovery_firmware.c
+ create mode 100644 libiscsi/tests/test_discovery_sendtargets.c
+ create mode 100644 libiscsi/tests/test_get_auth.c
+ create mode 100644 libiscsi/tests/test_get_initiator_name.c
+ create mode 100644 libiscsi/tests/test_get_network_config.c
+ create mode 100644 libiscsi/tests/test_login.c
+ create mode 100644 libiscsi/tests/test_logout.c
+ create mode 100644 libiscsi/tests/test_params.c
+ create mode 100644 libiscsi/tests/test_set_auth.c
+
+diff --git a/Makefile b/Makefile
+index c8cd00e09b00..cf028cfd078a 100644
+--- a/Makefile
++++ b/Makefile
+@@ -43,6 +43,7 @@ user: iscsiuio/Makefile
+ 	$(MAKE) -C usr
+ 	$(MAKE) -C utils
+ 	$(MAKE) -C iscsiuio
++	$(MAKE) -C libiscsi
+ 	@echo
+ 	@echo "Compilation complete                 Output file"
+ 	@echo "-----------------------------------  ----------------"
+@@ -71,6 +72,7 @@ kernel: force
+ force: ;
+ 
+ clean:
++	$(MAKE) -C libiscsi clean
+ 	$(MAKE) -C utils/sysdeps clean
+ 	$(MAKE) -C utils/fwparam_ibft clean
+ 	$(MAKE) -C utils clean
+diff --git a/libiscsi/Makefile b/libiscsi/Makefile
+new file mode 100644
+index 000000000000..317a7ec4db30
+--- /dev/null
++++ b/libiscsi/Makefile
+@@ -0,0 +1,61 @@
++# This Makefile will work only with GNU make.
++
++OSNAME=$(shell uname -s)
++OPTFLAGS ?= -O2 -g
++WARNFLAGS ?= -Wall -Wstrict-prototypes
++CFLAGS = $(OPTFLAGS) $(WARNFLAGS) -I../include -I../usr \
++		-D$(OSNAME) -fPIC -D_GNU_SOURCE -fvisibility=hidden
++LIB = libiscsi.so.0
++TESTS = tests/test_discovery_sendtargets tests/test_discovery_firmware
++TESTS += tests/test_login tests/test_logout tests/test_params
++TESTS += tests/test_get_network_config tests/test_get_initiator_name
++TESTS += tests/test_set_auth tests/test_get_auth
++
++COMMON_SRCS = sysdeps.o
++# sources shared between iscsid, iscsiadm and iscsistart
++ISCSI_LIB_SRCS = netlink.o transport.o cxgbi.o be2iscsi.o iscsi_timer.o initiator_common.o iscsi_err.o session_info.o iscsi_util.o io.o auth.o discovery.o login.o log.o md5.o sha1.o iface.o idbm.o sysfs.o iscsi_sysfs.o iscsi_net_util.o iscsid_req.o iser.o uip_mgmt_ipc.o
++FW_PARAM_SRCS = fw_entry.o prom_lex.o prom_parse.tab.o fwparam_ppc.o fwparam_sysfs.o
++
++# sources shared with the userspace utils, note we build these separately
++# to get PIC versions.
++COMMON_OBJS = $(patsubst %.o, common-objs/%.o, $(COMMON_SRCS))
++USR_OBJS = $(patsubst %.o, usr-objs/%.o, $(ISCSI_LIB_SRCS) strings.o)
++FW_OBJS = $(patsubst %.o, fw-objs/%.o, $(FW_PARAM_SRCS))
++
++# Flags for the tests
++tests/% : CFLAGS = $(OPTFLAGS) $(WARNFLAGS) -I.
++
++all: lib tests html
++
++lib: $(LIB)
++tests: $(TESTS)
++
++common-objs/%.o: ../utils/sysdeps/%.c
++	mkdir -p common-objs
++	$(CC) $(CFLAGS) -c $< -o $@
++
++usr-objs/%.o: ../usr/%.c
++	mkdir -p usr-objs
++	$(CC) $(CFLAGS) -c $< -o $@
++
++fw-objs/%.o: ../utils/fwparam_ibft/%.c
++	mkdir -p fw-objs
++	$(CC) $(CFLAGS) -c $< -o $@
++
++$(LIB): $(COMMON_OBJS) $(FW_OBJS) $(USR_OBJS) libiscsi.o
++	$(CC) $(CFLAGS) -shared -Wl,-soname,$(LIB) $^ -o $@
++	ln -s -f $(LIB) libiscsi.so
++
++$(TESTS): $(FW_OBJS) $(COMMON_OBJS) $(USR_OBJS) $(LIB)
++
++html: libiscsi.h libiscsi.doxy
++	doxygen libiscsi.doxy
++
++clean:
++	rm -rf *.o common-objs usr-objs fw-objs libuip-objs libiscsi.so* \
++			.depend *~ html $(TESTS) tests/*~
++
++depend:
++	gcc $(CFLAGS) -M `ls *.c` > .depend
++
++-include .depend ../usr/.depend
+diff --git a/libiscsi/libiscsi.c b/libiscsi/libiscsi.c
+new file mode 100644
+index 000000000000..6e6846a5162e
+--- /dev/null
++++ b/libiscsi/libiscsi.c
+@@ -0,0 +1,620 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include <unistd.h>
++#include <sys/syslog.h>
++#include "libiscsi.h"
++#include "idbm.h"
++#include "discovery.h"
++#include "log.h"
++#include "sysfs.h"
++#include "iscsi_sysfs.h"
++#include "session_info.h"
++#include "iscsi_util.h"
++#include "sysdeps.h"
++#include "iface.h"
++#include "iscsi_proto.h"
++#include "fw_context.h"
++#include "iscsid_req.h"
++#include "iscsi_err.h"
++
++#define CHECK(a) { context->error_str[0] = 0; rc = a; if (rc) goto leave; }
++
++/* UGLY, not thread safe :( */
++static int sysfs_initialized = 0;
++
++struct libiscsi_context {
++	char error_str[256];
++	/* For get_parameter_helper() */
++	const char *parameter;
++	char *value;
++};
++
++static void libiscsi_log(int prio, void *priv, const char *fmt, va_list ap)
++{
++	struct libiscsi_context *context = priv;
++
++	if (prio > LOG_ERR) /* We are only interested in errors (or worse) */
++		return;
++
++	vsnprintf(context->error_str, sizeof(context->error_str), fmt, ap);
++}
++
++struct libiscsi_context *libiscsi_init(void)
++{
++	struct libiscsi_context *context;
++
++	context = calloc(1, sizeof *context);
++	if (!context)
++		return NULL;
++
++	log_init("libiscsi", 1024, libiscsi_log, context);
++	if (!sysfs_initialized) {
++		sysfs_init();
++		sysfs_initialized = 1;
++	}
++	increase_max_files();
++	if (idbm_init(NULL)) {
++		sysfs_cleanup();
++		free(context);
++		return NULL;
++	}
++
++	iface_setup_host_bindings();
++
++	return context;
++}
++
++void libiscsi_cleanup(struct libiscsi_context *context)
++{
++	idbm_terminate();
++	free_transports();
++	sysfs_cleanup();
++	free(context);
++}
++
++static void free_iface_list(struct list_head *ifaces)
++{
++	struct iface_rec *iface, *tmp_iface;
++
++	list_for_each_entry_safe(iface, tmp_iface, ifaces, list) {
++		list_del(&iface->list);
++		free(iface);
++	}
++}
++
++static void free_rec_list(struct list_head *rec_list)
++{
++	struct node_rec *rec, *tmp;
++	
++	list_for_each_entry_safe(rec, tmp, rec_list, list) {
++		list_del(&rec->list);
++		free(rec);
++	}
++}
++
++int libiscsi_discover_sendtargets(struct libiscsi_context *context,
++	const char *address, int port,
++	const struct libiscsi_auth_info *auth_info,
++	int *nr_found, struct libiscsi_node **found_nodes)
++{
++	struct discovery_rec drec;
++	LIST_HEAD(bound_rec_list);
++	struct node_rec *rec;
++	int rc = 0, found = 0;
++
++	INIT_LIST_HEAD(&bound_rec_list);
++
++	if (nr_found)
++		*nr_found = 0;
++	if (found_nodes)
++		*found_nodes = NULL;
++
++	CHECK(libiscsi_verify_auth_info(context, auth_info))
++
++	/* Fill the drec struct with all needed info */
++	memset(&drec, 0, sizeof drec);
++	idbm_sendtargets_defaults(&drec.u.sendtargets);
++	drec.type = DISCOVERY_TYPE_SENDTARGETS;
++	strlcpy(drec.address, address, sizeof(drec.address));
++	drec.port = port ? port : ISCSI_LISTEN_PORT;
++	switch(auth_info ? auth_info->method : libiscsi_auth_none) {
++	case libiscsi_auth_chap:
++		drec.u.sendtargets.auth.authmethod = AUTH_METHOD_CHAP;
++		strlcpy(drec.u.sendtargets.auth.username,
++			auth_info->chap.username, AUTH_STR_MAX_LEN);
++		strlcpy((char *)drec.u.sendtargets.auth.password,
++			auth_info->chap.password, AUTH_STR_MAX_LEN);
++		drec.u.sendtargets.auth.password_length =
++			strlen((char *)drec.u.sendtargets.auth.password);
++		strlcpy(drec.u.sendtargets.auth.username_in,
++			auth_info->chap.reverse_username, AUTH_STR_MAX_LEN);
++		strlcpy((char *)drec.u.sendtargets.auth.password_in,
++			auth_info->chap.reverse_password, AUTH_STR_MAX_LEN);
++		drec.u.sendtargets.auth.password_in_length =
++			strlen((char *)drec.u.sendtargets.auth.password_in);
++		break;
++	}
++
++	CHECK(idbm_add_discovery(&drec))
++
++	CHECK(idbm_bind_ifaces_to_nodes(discovery_sendtargets,
++					&drec, NULL, &bound_rec_list))
++
++	/* now add/update records */
++	list_for_each_entry(rec, &bound_rec_list, list) {
++		CHECK(idbm_add_node(rec, &drec, 1 /* overwrite */))
++		found++;
++	}
++
++	if (nr_found)
++		*nr_found = found;
++
++	if (found_nodes && found) {
++		*found_nodes = calloc(found, sizeof **found_nodes);
++		if (*found_nodes == NULL) {
++			snprintf(context->error_str,
++				 sizeof(context->error_str), strerror(ENOMEM));
++			rc = ENOMEM;
++			goto leave;
++		}
++		found = 0;
++		list_for_each_entry(rec, &bound_rec_list, list) {
++			strlcpy((*found_nodes)[found].name, rec->name,
++				 LIBISCSI_VALUE_MAXLEN);
++			(*found_nodes)[found].tpgt = rec->tpgt;
++			strlcpy((*found_nodes)[found].address,
++				 rec->conn[0].address, NI_MAXHOST);
++			(*found_nodes)[found].port = rec->conn[0].port;
++			strlcpy((*found_nodes)[found].iface,
++				 rec->iface.name, LIBISCSI_VALUE_MAXLEN);
++			found++;
++		}
++	}
++
++leave:
++	free_rec_list(&bound_rec_list);
++	return rc;
++}
++
++int libiscsi_discover_firmware(struct libiscsi_context *context,
++	int *nr_found, struct libiscsi_node **found_nodes)
++{
++	struct list_head targets, ifaces, rec_list;
++	discovery_rec_t drec;
++	int rc = 0;
++
++	INIT_LIST_HEAD(&targets);
++	INIT_LIST_HEAD(&ifaces);
++	INIT_LIST_HEAD(&rec_list);
++
++	if (nr_found) {
++		*nr_found = 0;
++	}
++
++	if (found_nodes) {
++		*found_nodes = NULL;
++	}
++
++	rc = fw_get_targets(&targets);
++	if (rc) {
++		log_error("%s: Could not get list of targets from firmware "
++			  "(err %d).\n", __func__, rc);
++		return rc;
++	}
++
++	CHECK(iface_create_ifaces_from_boot_contexts(&ifaces, &targets));
++
++	memset(&drec, 0, sizeof(drec));
++	drec.type = DISCOVERY_TYPE_FW;
++	rc = idbm_bind_ifaces_to_nodes(discovery_fw, &drec, &ifaces, &rec_list);
++	if (rc) {
++		log_error("%s: Could not determine target nodes from firmware "
++			  "(err %d).\n", __func__, rc);
++		goto leave;
++	}
++
++	int node_count = 0;
++	struct list_head *pos;
++	list_for_each(pos, &rec_list) {
++		++node_count;
++	}
++
++	struct libiscsi_node* new_nodes;
++	/* allocate enough space for all the nodes */
++	new_nodes = calloc(node_count, sizeof *new_nodes);
++	if (new_nodes == NULL) {
++		rc = ENOMEM;
++		log_error("%s: %s.\n", __func__, strerror(ENOMEM));
++		goto leave;
++	}
++
++	struct node_rec *rec;
++	struct libiscsi_node *new_node = new_nodes;
++	/* in one loop, add nodes to idbm and create libiscsi_node entries */
++	list_for_each_entry(rec, &rec_list, list) {
++		CHECK(idbm_add_node(rec, NULL, 1 /* overwrite */));
++
++		strlcpy(new_node->name, rec->name, LIBISCSI_VALUE_MAXLEN);
++		new_node->tpgt = rec->tpgt;
++		strlcpy(new_node->address, rec->conn[0].address, NI_MAXHOST);
++		new_node->port = rec->conn[0].port;
++		strlcpy(new_node->iface, rec->iface.name, LIBISCSI_VALUE_MAXLEN);
++
++		++new_node;
++	}
++
++	/* update output parameters */
++	if (nr_found) {
++		*nr_found = node_count;
++	}
++	if (found_nodes) {
++		*found_nodes = new_nodes;
++	}
++
++leave:
++	fw_free_targets(&targets);
++
++	free_iface_list(&ifaces);
++	free_rec_list(&rec_list);
++
++	return rc;
++}
++
++int libiscsi_verify_auth_info(struct libiscsi_context *context,
++	const struct libiscsi_auth_info *auth_info)
++{
++	switch(auth_info ? auth_info->method : libiscsi_auth_none) {
++	case libiscsi_auth_none:
++		break;
++	case libiscsi_auth_chap:
++		if (!auth_info->chap.username[0]) {
++			strcpy(context->error_str, "Empty username");
++			return EINVAL;
++		}
++		if (!auth_info->chap.password[0]) {
++			strcpy(context->error_str, "Empty password");
++			return EINVAL;
++		}
++		if (auth_info->chap.reverse_username[0] &&
++		    !auth_info->chap.reverse_password[0]) {
++			strcpy(context->error_str, "Empty reverse password");
++		    	return EINVAL;
++		}
++		break;
++	default:
++		sprintf(context->error_str,
++			"Invalid authentication method: %d",
++			(int)auth_info->method);
++		return EINVAL;
++	}
++	return 0;
++}
++
++int libiscsi_node_set_auth(struct libiscsi_context *context,
++    const struct libiscsi_node *node,
++    const struct libiscsi_auth_info *auth_info)
++{
++	int rc = 0;
++
++	CHECK(libiscsi_verify_auth_info(context, auth_info))
++
++	switch(auth_info ? auth_info->method : libiscsi_auth_none) {
++	case libiscsi_auth_none:
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.authmethod", "None"))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.username", ""))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.password", ""))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.username_in", ""))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.password_in", ""))
++		break;
++
++	case libiscsi_auth_chap:
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.authmethod", "CHAP"))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.username",
++			auth_info->chap.username))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.password",
++			auth_info->chap.password))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.username_in",
++			auth_info->chap.reverse_username))
++		CHECK(libiscsi_node_set_parameter(context, node,
++			"node.session.auth.password_in",
++			auth_info->chap.reverse_password))
++		break;
++	}
++leave:
++	return rc;
++}
++
++int libiscsi_node_get_auth(struct libiscsi_context *context,
++    const struct libiscsi_node *node,
++    struct libiscsi_auth_info *auth_info)
++{
++	int rc = 0;
++	char value[LIBISCSI_VALUE_MAXLEN];
++
++	memset(auth_info, 0, sizeof *auth_info);
++
++	CHECK(libiscsi_node_get_parameter(context, node,
++			"node.session.auth.authmethod", value))
++
++	if (!strcmp(value, "None")) {
++		auth_info->method = libiscsi_auth_none;
++	} else if (!strcmp(value, "CHAP")) {
++		auth_info->method = libiscsi_auth_chap;
++		CHECK(libiscsi_node_get_parameter(context, node,
++			"node.session.auth.username",
++			auth_info->chap.username))
++		CHECK(libiscsi_node_get_parameter(context, node,
++			"node.session.auth.password",
++			auth_info->chap.password))
++		CHECK(libiscsi_node_get_parameter(context, node,
++			"node.session.auth.username_in",
++			auth_info->chap.reverse_username))
++		CHECK(libiscsi_node_get_parameter(context, node,
++			"node.session.auth.password_in",
++			auth_info->chap.reverse_password))
++	} else {
++		snprintf(context->error_str, sizeof(context->error_str),
++			 "unknown authentication method: %s", value);
++		rc = EINVAL;
++	}
++leave:
++	return rc;
++}
++
++static void node_to_rec(const struct libiscsi_node *node,
++	struct node_rec *rec)
++{
++	memset(rec, 0, sizeof *rec);
++	idbm_node_setup_defaults(rec);
++	strlcpy(rec->name, node->name, TARGET_NAME_MAXLEN);
++	rec->tpgt = node->tpgt;
++	strlcpy(rec->conn[0].address, node->address, NI_MAXHOST);
++	rec->conn[0].port = node->port;
++}
++
++int login_helper(void *data, node_rec_t *rec)
++{
++	char *iface = (char*)data;
++	if (strcmp(iface, rec->iface.name))
++		/* different iface, skip it */
++		return -1;
++
++	int rc = iscsid_req_by_rec(MGMT_IPC_SESSION_LOGIN, rec);
++	if (rc) {
++		iscsi_err_print_msg(rc);
++		rc = ENOTCONN;
++	}
++	return rc;
++}
++
++int libiscsi_node_login(struct libiscsi_context *context,
++	const struct libiscsi_node *node)
++{
++	int nr_found = 0, rc;
++
++	CHECK(idbm_for_each_iface(&nr_found, (void*)node->iface, login_helper,
++		(char *)node->name, node->tpgt,
++		(char *)node->address, node->port))
++	if (nr_found == 0) {
++		strcpy(context->error_str, "No such node");
++		rc = ENODEV;
++	}
++leave:
++	return rc;
++}
++
++static int logout_helper(void *data, struct session_info *info)
++{
++	int rc;
++	struct node_rec *rec = data;
++
++	if (!iscsi_match_session(rec, info))
++		/* Tell iscsi_sysfs_for_each_session this session was not a
++		   match so that it will not increase nr_found. */
++		return -1;
++
++	rc = iscsid_req_by_sid(MGMT_IPC_SESSION_LOGOUT, info->sid);
++	if (rc) {
++		iscsi_err_print_msg(rc);
++		rc = EIO;
++	}
++
++	return rc;
++}
++
++int libiscsi_node_logout(struct libiscsi_context *context,
++	const struct libiscsi_node *node)
++{
++	int nr_found = 0, rc;
++	struct node_rec rec;
++
++	node_to_rec(node, &rec);
++	CHECK(iscsi_sysfs_for_each_session(&rec, &nr_found, logout_helper,0))
++	if (nr_found == 0) {
++		strcpy(context->error_str, "No matching session");
++		rc = ENODEV;
++	}
++leave:
++	return rc;
++}
++
++int libiscsi_node_set_parameter(struct libiscsi_context *context,
++	const struct libiscsi_node *node,
++	const char *parameter, const char *value)
++{
++	int nr_found = 0, rc;
++	struct user_param *param;
++	struct list_head params;
++
++	INIT_LIST_HEAD(&params);
++	param = idbm_alloc_user_param(parameter, value);
++	if (!param) {
++		rc = ENOMEM;
++		goto leave;
++	}
++	list_add_tail(&params, &param->list);
++
++	CHECK(idbm_for_each_iface(&nr_found, &params, idbm_node_set_param,
++		(char *)node->name, node->tpgt,
++		(char *)node->address, node->port))
++	if (nr_found == 0) {
++		strcpy(context->error_str, "No such node");
++		rc = ENODEV;
++	}
++	free(param->name);
++	free(param);
++leave:
++	return rc;
++}
++
++static int get_parameter_helper(void *data, node_rec_t *rec)
++{
++	struct libiscsi_context *context = data;
++	recinfo_t *info;
++	int i;
++
++	info = idbm_recinfo_alloc(MAX_KEYS);
++	if (!info) {
++		snprintf(context->error_str, sizeof(context->error_str),
++			 strerror(ENOMEM));
++		return ENOMEM;
++	}
++
++	idbm_recinfo_node(rec, info);
++
++	for (i = 0; i < MAX_KEYS; i++) {
++		if (!info[i].visible)
++			continue;
++
++		if (strcmp(context->parameter, info[i].name))
++			continue;
++
++		strlcpy(context->value, info[i].value, LIBISCSI_VALUE_MAXLEN);
++		break;
++	}
++
++	free(info);
++
++	if (i == MAX_KEYS) {
++		strcpy(context->error_str, "No such parameter");
++		return EINVAL;
++	}
++
++	return 0;
++}
++
++int libiscsi_node_get_parameter(struct libiscsi_context *context,
++	const struct libiscsi_node *node, const char *parameter, char *value)
++{
++	int nr_found = 0, rc = 0;
++
++	context->parameter = parameter;
++	context->value = value;
++
++	/* Note we assume there is only one interface, if not we will get
++	   the value from the last interface iterated over!
++	   This (multiple interfaces) can only happen if someone explicitly
++	   created ones using iscsiadm. Even then this should not be a problem
++	   as most settings should be the same independent of the iface. */
++	CHECK(idbm_for_each_iface(&nr_found, context, get_parameter_helper,
++		(char *)node->name, node->tpgt,
++		(char *)node->address, node->port))
++	if (nr_found == 0) {
++		strcpy(context->error_str, "No such node");
++		rc = ENODEV;
++	}
++leave:
++	return rc;
++}
++
++const char *libiscsi_get_error_string(struct libiscsi_context *context)
++{
++	/* Sometimes the core open-iscsi code does not give us an error
++	   message */
++	if (!context->error_str[0])
++		return "Unknown error";
++
++	return context->error_str;
++}
++
++
++/************************** Utility functions *******************************/
++
++int libiscsi_get_firmware_network_config(
++    struct libiscsi_network_config *config)
++{
++	struct boot_context fw_entry;
++
++	if (!sysfs_initialized) {
++		sysfs_init();
++		sysfs_initialized = 1;
++	}
++
++	memset(config, 0, sizeof *config);
++	memset(&fw_entry, 0, sizeof fw_entry);
++	if (fw_get_entry(&fw_entry))
++		return ENODEV;
++
++	config->dhcp = strlen(fw_entry.dhcp) ? 1 : 0;
++	strncpy(config->iface_name, fw_entry.iface, sizeof fw_entry.iface);
++	strncpy(config->mac_address, fw_entry.mac, sizeof fw_entry.mac);
++	strncpy(config->ip_address, fw_entry.ipaddr, sizeof fw_entry.ipaddr);
++	strncpy(config->netmask, fw_entry.mask, sizeof fw_entry.mask);
++	strncpy(config->gateway, fw_entry.gateway, sizeof fw_entry.gateway);
++	strncpy(config->primary_dns, fw_entry.primary_dns,
++		sizeof fw_entry.primary_dns);
++	strncpy(config->secondary_dns, fw_entry.secondary_dns,
++		sizeof fw_entry.secondary_dns);
++	return 0;
++}
++
++int libiscsi_get_firmware_initiator_name(char *initiatorname)
++{
++	struct boot_context fw_entry;
++
++	if (!sysfs_initialized) {
++		sysfs_init();
++		sysfs_initialized = 1;
++	}
++
++	memset(initiatorname, 0, LIBISCSI_VALUE_MAXLEN);
++	memset(&fw_entry, 0, sizeof fw_entry);
++	if (fw_get_entry(&fw_entry))
++		return ENODEV;
++
++	strncpy(initiatorname, fw_entry.initiatorname,
++		sizeof fw_entry.initiatorname);
++
++	return 0;
++}
+diff --git a/libiscsi/libiscsi.doxy b/libiscsi/libiscsi.doxy
+new file mode 100644
+index 000000000000..663770f3e939
+--- /dev/null
++++ b/libiscsi/libiscsi.doxy
+@@ -0,0 +1,1473 @@
++# Doxyfile 1.5.7.1
++
++# This file describes the settings to be used by the documentation system
++# doxygen (www.doxygen.org) for a project
++#
++# All text after a hash (#) is considered a comment and will be ignored
++# The format is:
++#       TAG = value [value, ...]
++# For lists items can also be appended using:
++#       TAG += value [value, ...]
++# Values that contain spaces should be placed between quotes (" ")
++
++#---------------------------------------------------------------------------
++# Project related configuration options
++#---------------------------------------------------------------------------
++
++# This tag specifies the encoding used for all characters in the config file 
++# that follow. The default is UTF-8 which is also the encoding used for all 
++# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
++# iconv built into libc) for the transcoding. See 
++# http://www.gnu.org/software/libiconv for the list of possible encodings.
++
++DOXYFILE_ENCODING      = UTF-8
++
++# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
++# by quotes) that should identify the project.
++
++PROJECT_NAME           = libiscsi
++
++# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
++# This could be handy for archiving the generated documentation or 
++# if some version control system is used.
++
++PROJECT_NUMBER         = 
++
++# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
++# base path where the generated documentation will be put. 
++# If a relative path is entered, it will be relative to the location 
++# where doxygen was started. If left blank the current directory will be used.
++
++OUTPUT_DIRECTORY       = 
++
++# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
++# 4096 sub-directories (in 2 levels) under the output directory of each output 
++# format and will distribute the generated files over these directories. 
++# Enabling this option can be useful when feeding doxygen a huge amount of 
++# source files, where putting all generated files in the same directory would 
++# otherwise cause performance problems for the file system.
++
++CREATE_SUBDIRS         = NO
++
++# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
++# documentation generated by doxygen is written. Doxygen will use this 
++# information to generate all constant output in the proper language. 
++# The default language is English, other supported languages are: 
++# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
++# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, 
++# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), 
++# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, 
++# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, 
++# Spanish, Swedish, and Ukrainian.
++
++OUTPUT_LANGUAGE        = English
++
++# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
++# include brief member descriptions after the members that are listed in 
++# the file and class documentation (similar to JavaDoc). 
++# Set to NO to disable this.
++
++BRIEF_MEMBER_DESC      = YES
++
++# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
++# the brief description of a member or function before the detailed description. 
++# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
++# brief descriptions will be completely suppressed.
++
++REPEAT_BRIEF           = NO
++
++# This tag implements a quasi-intelligent brief description abbreviator 
++# that is used to form the text in various listings. Each string 
++# in this list, if found as the leading text of the brief description, will be 
++# stripped from the text and the result after processing the whole list, is 
++# used as the annotated text. Otherwise, the brief description is used as-is. 
++# If left blank, the following values are used ("$name" is automatically 
++# replaced with the name of the entity): "The $name class" "The $name widget" 
++# "The $name file" "is" "provides" "specifies" "contains" 
++# "represents" "a" "an" "the"
++
++ABBREVIATE_BRIEF       = 
++
++# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
++# Doxygen will generate a detailed section even if there is only a brief 
++# description.
++
++ALWAYS_DETAILED_SEC    = YES
++
++# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
++# inherited members of a class in the documentation of that class as if those 
++# members were ordinary class members. Constructors, destructors and assignment 
++# operators of the base classes will not be shown.
++
++INLINE_INHERITED_MEMB  = NO
++
++# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
++# path before files name in the file list and in the header files. If set 
++# to NO the shortest path that makes the file name unique will be used.
++
++FULL_PATH_NAMES        = YES
++
++# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
++# can be used to strip a user-defined part of the path. Stripping is 
++# only done if one of the specified strings matches the left-hand part of 
++# the path. The tag can be used to show relative paths in the file list. 
++# If left blank the directory from which doxygen is run is used as the 
++# path to strip.
++
++STRIP_FROM_PATH        = 
++
++# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
++# the path mentioned in the documentation of a class, which tells 
++# the reader which header file to include in order to use a class. 
++# If left blank only the name of the header file containing the class 
++# definition is used. Otherwise one should specify the include paths that 
++# are normally passed to the compiler using the -I flag.
++
++STRIP_FROM_INC_PATH    = 
++
++# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
++# (but less readable) file names. This can be useful is your file systems 
++# doesn't support long names like on DOS, Mac, or CD-ROM.
++
++SHORT_NAMES            = NO
++
++# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
++# will interpret the first line (until the first dot) of a JavaDoc-style 
++# comment as the brief description. If set to NO, the JavaDoc 
++# comments will behave just like regular Qt-style comments 
++# (thus requiring an explicit @brief command for a brief description.)
++
++JAVADOC_AUTOBRIEF      = NO
++
++# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
++# interpret the first line (until the first dot) of a Qt-style 
++# comment as the brief description. If set to NO, the comments 
++# will behave just like regular Qt-style comments (thus requiring 
++# an explicit \brief command for a brief description.)
++
++QT_AUTOBRIEF           = NO
++
++# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
++# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
++# comments) as a brief description. This used to be the default behaviour. 
++# The new default is to treat a multi-line C++ comment block as a detailed 
++# description. Set this tag to YES if you prefer the old behaviour instead.
++
++MULTILINE_CPP_IS_BRIEF = NO
++
++# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
++# member inherits the documentation from any documented member that it 
++# re-implements.
++
++INHERIT_DOCS           = YES
++
++# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
++# a new page for each member. If set to NO, the documentation of a member will 
++# be part of the file/class/namespace that contains it.
++
++SEPARATE_MEMBER_PAGES  = NO
++
++# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
++# Doxygen uses this value to replace tabs by spaces in code fragments.
++
++TAB_SIZE               = 8
++
++# This tag can be used to specify a number of aliases that acts 
++# as commands in the documentation. An alias has the form "name=value". 
++# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
++# put the command \sideeffect (or @sideeffect) in the documentation, which 
++# will result in a user-defined paragraph with heading "Side Effects:". 
++# You can put \n's in the value part of an alias to insert newlines.
++
++ALIASES                = 
++
++# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
++# sources only. Doxygen will then generate output that is more tailored for C. 
++# For instance, some of the names that are used will be different. The list 
++# of all members will be omitted, etc.
++
++OPTIMIZE_OUTPUT_FOR_C  = YES
++
++# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
++# sources only. Doxygen will then generate output that is more tailored for 
++# Java. For instance, namespaces will be presented as packages, qualified 
++# scopes will look different, etc.
++
++OPTIMIZE_OUTPUT_JAVA   = NO
++
++# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
++# sources only. Doxygen will then generate output that is more tailored for 
++# Fortran.
++
++OPTIMIZE_FOR_FORTRAN   = NO
++
++# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
++# sources. Doxygen will then generate output that is tailored for 
++# VHDL.
++
++OPTIMIZE_OUTPUT_VHDL   = NO
++
++# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
++# to include (a tag file for) the STL sources as input, then you should 
++# set this tag to YES in order to let doxygen match functions declarations and 
++# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
++# func(std::string) {}). This also make the inheritance and collaboration 
++# diagrams that involve STL classes more complete and accurate.
++
++BUILTIN_STL_SUPPORT    = NO
++
++# If you use Microsoft's C++/CLI language, you should set this option to YES to
++# enable parsing support.
++
++CPP_CLI_SUPPORT        = NO
++
++# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
++# Doxygen will parse them like normal C++ but will assume all classes use public 
++# instead of private inheritance when no explicit protection keyword is present.
++
++SIP_SUPPORT            = NO
++
++# For Microsoft's IDL there are propget and propput attributes to indicate getter 
++# and setter methods for a property. Setting this option to YES (the default) 
++# will make doxygen to replace the get and set methods by a property in the 
++# documentation. This will only work if the methods are indeed getting or 
++# setting a simple type. If this is not the case, or you want to show the 
++# methods anyway, you should set this option to NO.
++
++IDL_PROPERTY_SUPPORT   = YES
++
++# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
++# tag is set to YES, then doxygen will reuse the documentation of the first 
++# member in the group (if any) for the other members of the group. By default 
++# all members of a group must be documented explicitly.
++
++DISTRIBUTE_GROUP_DOC   = NO
++
++# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
++# the same type (for instance a group of public functions) to be put as a 
++# subgroup of that type (e.g. under the Public Functions section). Set it to 
++# NO to prevent subgrouping. Alternatively, this can be done per class using 
++# the \nosubgrouping command.
++
++SUBGROUPING            = YES
++
++# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
++# is documented as struct, union, or enum with the name of the typedef. So 
++# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
++# with name TypeT. When disabled the typedef will appear as a member of a file, 
++# namespace, or class. And the struct will be named TypeS. This can typically 
++# be useful for C code in case the coding convention dictates that all compound 
++# types are typedef'ed and only the typedef is referenced, never the tag name.
++
++TYPEDEF_HIDES_STRUCT   = NO
++
++# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 
++# determine which symbols to keep in memory and which to flush to disk.
++# When the cache is full, less often used symbols will be written to disk.
++# For small to medium size projects (<1000 input files) the default value is 
++# probably good enough. For larger projects a too small cache size can cause 
++# doxygen to be busy swapping symbols to and from disk most of the time 
++# causing a significant performance penality. 
++# If the system has enough physical memory increasing the cache will improve the 
++# performance by keeping more symbols in memory. Note that the value works on 
++# a logarithmic scale so increasing the size by one will rougly double the 
++# memory usage. The cache size is given by this formula: 
++# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
++# corresponding to a cache size of 2^16 = 65536 symbols
++
++SYMBOL_CACHE_SIZE      = 0
++
++#---------------------------------------------------------------------------
++# Build related configuration options
++#---------------------------------------------------------------------------
++
++# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
++# documentation are documented, even if no documentation was available. 
++# Private class members and static file members will be hidden unless 
++# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
++
++EXTRACT_ALL            = YES
++
++# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
++# will be included in the documentation.
++
++EXTRACT_PRIVATE        = NO
++
++# If the EXTRACT_STATIC tag is set to YES all static members of a file 
++# will be included in the documentation.
++
++EXTRACT_STATIC         = NO
++
++# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
++# defined locally in source files will be included in the documentation. 
++# If set to NO only classes defined in header files are included.
++
++EXTRACT_LOCAL_CLASSES  = YES
++
++# This flag is only useful for Objective-C code. When set to YES local 
++# methods, which are defined in the implementation section but not in 
++# the interface are included in the documentation. 
++# If set to NO (the default) only methods in the interface are included.
++
++EXTRACT_LOCAL_METHODS  = NO
++
++# If this flag is set to YES, the members of anonymous namespaces will be 
++# extracted and appear in the documentation as a namespace called 
++# 'anonymous_namespace{file}', where file will be replaced with the base 
++# name of the file that contains the anonymous namespace. By default 
++# anonymous namespace are hidden.
++
++EXTRACT_ANON_NSPACES   = NO
++
++# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
++# undocumented members of documented classes, files or namespaces. 
++# If set to NO (the default) these members will be included in the 
++# various overviews, but no documentation section is generated. 
++# This option has no effect if EXTRACT_ALL is enabled.
++
++HIDE_UNDOC_MEMBERS     = NO
++
++# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
++# undocumented classes that are normally visible in the class hierarchy. 
++# If set to NO (the default) these classes will be included in the various 
++# overviews. This option has no effect if EXTRACT_ALL is enabled.
++
++HIDE_UNDOC_CLASSES     = NO
++
++# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
++# friend (class|struct|union) declarations. 
++# If set to NO (the default) these declarations will be included in the 
++# documentation.
++
++HIDE_FRIEND_COMPOUNDS  = NO
++
++# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
++# documentation blocks found inside the body of a function. 
++# If set to NO (the default) these blocks will be appended to the 
++# function's detailed documentation block.
++
++HIDE_IN_BODY_DOCS      = NO
++
++# The INTERNAL_DOCS tag determines if documentation 
++# that is typed after a \internal command is included. If the tag is set 
++# to NO (the default) then the documentation will be excluded. 
++# Set it to YES to include the internal documentation.
++
++INTERNAL_DOCS          = NO
++
++# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
++# file names in lower-case letters. If set to YES upper-case letters are also 
++# allowed. This is useful if you have classes or files whose names only differ 
++# in case and if your file system supports case sensitive file names. Windows 
++# and Mac users are advised to set this option to NO.
++
++CASE_SENSE_NAMES       = YES
++
++# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
++# will show members with their full class and namespace scopes in the 
++# documentation. If set to YES the scope will be hidden.
++
++HIDE_SCOPE_NAMES       = NO
++
++# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
++# will put a list of the files that are included by a file in the documentation 
++# of that file.
++
++SHOW_INCLUDE_FILES     = YES
++
++# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
++# is inserted in the documentation for inline members.
++
++INLINE_INFO            = YES
++
++# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
++# will sort the (detailed) documentation of file and class members 
++# alphabetically by member name. If set to NO the members will appear in 
++# declaration order.
++
++SORT_MEMBER_DOCS       = YES
++
++# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
++# brief documentation of file, namespace and class members alphabetically 
++# by member name. If set to NO (the default) the members will appear in 
++# declaration order.
++
++SORT_BRIEF_DOCS        = NO
++
++# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
++# hierarchy of group names into alphabetical order. If set to NO (the default) 
++# the group names will appear in their defined order.
++
++SORT_GROUP_NAMES       = NO
++
++# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
++# sorted by fully-qualified names, including namespaces. If set to 
++# NO (the default), the class list will be sorted only by class name, 
++# not including the namespace part. 
++# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
++# Note: This option applies only to the class list, not to the 
++# alphabetical list.
++
++SORT_BY_SCOPE_NAME     = NO
++
++# The GENERATE_TODOLIST tag can be used to enable (YES) or 
++# disable (NO) the todo list. This list is created by putting \todo 
++# commands in the documentation.
++
++GENERATE_TODOLIST      = YES
++
++# The GENERATE_TESTLIST tag can be used to enable (YES) or 
++# disable (NO) the test list. This list is created by putting \test 
++# commands in the documentation.
++
++GENERATE_TESTLIST      = YES
++
++# The GENERATE_BUGLIST tag can be used to enable (YES) or 
++# disable (NO) the bug list. This list is created by putting \bug 
++# commands in the documentation.
++
++GENERATE_BUGLIST       = YES
++
++# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
++# disable (NO) the deprecated list. This list is created by putting 
++# \deprecated commands in the documentation.
++
++GENERATE_DEPRECATEDLIST= YES
++
++# The ENABLED_SECTIONS tag can be used to enable conditional 
++# documentation sections, marked by \if sectionname ... \endif.
++
++ENABLED_SECTIONS       = 
++
++# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
++# the initial value of a variable or define consists of for it to appear in 
++# the documentation. If the initializer consists of more lines than specified 
++# here it will be hidden. Use a value of 0 to hide initializers completely. 
++# The appearance of the initializer of individual variables and defines in the 
++# documentation can be controlled using \showinitializer or \hideinitializer 
++# command in the documentation regardless of this setting.
++
++MAX_INITIALIZER_LINES  = 30
++
++# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
++# at the bottom of the documentation of classes and structs. If set to YES the 
++# list will mention the files that were used to generate the documentation.
++
++SHOW_USED_FILES        = YES
++
++# If the sources in your project are distributed over multiple directories 
++# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
++# in the documentation. The default is NO.
++
++SHOW_DIRECTORIES       = NO
++
++# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
++# This will remove the Files entry from the Quick Index and from the 
++# Folder Tree View (if specified). The default is YES.
++
++SHOW_FILES             = YES
++
++# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
++# Namespaces page.  This will remove the Namespaces entry from the Quick Index
++# and from the Folder Tree View (if specified). The default is YES.
++
++SHOW_NAMESPACES        = YES
++
++# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
++# doxygen should invoke to get the current version for each file (typically from 
++# the version control system). Doxygen will invoke the program by executing (via 
++# popen()) the command <command> <input-file>, where <command> is the value of 
++# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
++# provided by doxygen. Whatever the program writes to standard output 
++# is used as the file version. See the manual for examples.
++
++FILE_VERSION_FILTER    = 
++
++# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by 
++# doxygen. The layout file controls the global structure of the generated output files 
++# in an output format independent way. The create the layout file that represents 
++# doxygen's defaults, run doxygen with the -l option. You can optionally specify a 
++# file name after the option, if omitted DoxygenLayout.xml will be used as the name 
++# of the layout file.
++
++LAYOUT_FILE            = 
++
++#---------------------------------------------------------------------------
++# configuration options related to warning and progress messages
++#---------------------------------------------------------------------------
++
++# The QUIET tag can be used to turn on/off the messages that are generated 
++# by doxygen. Possible values are YES and NO. If left blank NO is used.
++
++QUIET                  = YES
++
++# The WARNINGS tag can be used to turn on/off the warning messages that are 
++# generated by doxygen. Possible values are YES and NO. If left blank 
++# NO is used.
++
++WARNINGS               = YES
++
++# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
++# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
++# automatically be disabled.
++
++WARN_IF_UNDOCUMENTED   = YES
++
++# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
++# potential errors in the documentation, such as not documenting some 
++# parameters in a documented function, or documenting parameters that 
++# don't exist or using markup commands wrongly.
++
++WARN_IF_DOC_ERROR      = YES
++
++# This WARN_NO_PARAMDOC option can be abled to get warnings for 
++# functions that are documented, but have no documentation for their parameters 
++# or return value. If set to NO (the default) doxygen will only warn about 
++# wrong or incomplete parameter documentation, but not about the absence of 
++# documentation.
++
++WARN_NO_PARAMDOC       = NO
++
++# The WARN_FORMAT tag determines the format of the warning messages that 
++# doxygen can produce. The string should contain the $file, $line, and $text 
++# tags, which will be replaced by the file and line number from which the 
++# warning originated and the warning text. Optionally the format may contain 
++# $version, which will be replaced by the version of the file (if it could 
++# be obtained via FILE_VERSION_FILTER)
++
++WARN_FORMAT            = "$file:$line: $text"
++
++# The WARN_LOGFILE tag can be used to specify a file to which warning 
++# and error messages should be written. If left blank the output is written 
++# to stderr.
++
++WARN_LOGFILE           = 
++
++#---------------------------------------------------------------------------
++# configuration options related to the input files
++#---------------------------------------------------------------------------
++
++# The INPUT tag can be used to specify the files and/or directories that contain 
++# documented source files. You may enter file names like "myfile.cpp" or 
++# directories like "/usr/src/myproject". Separate the files or directories 
++# with spaces.
++
++INPUT                  = 
++
++# This tag can be used to specify the character encoding of the source files 
++# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
++# also the default input encoding. Doxygen uses libiconv (or the iconv built 
++# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
++# the list of possible encodings.
++
++INPUT_ENCODING         = UTF-8
++
++# If the value of the INPUT tag contains directories, you can use the 
++# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
++# and *.h) to filter out the source-files in the directories. If left 
++# blank the following patterns are tested: 
++# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
++# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
++
++FILE_PATTERNS          = 
++
++# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
++# should be searched for input files as well. Possible values are YES and NO. 
++# If left blank NO is used.
++
++RECURSIVE              = NO
++
++# The EXCLUDE tag can be used to specify files and/or directories that should 
++# excluded from the INPUT source files. This way you can easily exclude a 
++# subdirectory from a directory tree whose root is specified with the INPUT tag.
++
++EXCLUDE                = 
++
++# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
++# directories that are symbolic links (a Unix filesystem feature) are excluded 
++# from the input.
++
++EXCLUDE_SYMLINKS       = NO
++
++# If the value of the INPUT tag contains directories, you can use the 
++# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
++# certain files from those directories. Note that the wildcards are matched 
++# against the file with absolute path, so to exclude all test directories 
++# for example use the pattern */test/*
++
++EXCLUDE_PATTERNS       = 
++
++# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
++# (namespaces, classes, functions, etc.) that should be excluded from the 
++# output. The symbol name can be a fully qualified name, a word, or if the 
++# wildcard * is used, a substring. Examples: ANamespace, AClass, 
++# AClass::ANamespace, ANamespace::*Test
++
++EXCLUDE_SYMBOLS        = 
++
++# The EXAMPLE_PATH tag can be used to specify one or more files or 
++# directories that contain example code fragments that are included (see 
++# the \include command).
++
++EXAMPLE_PATH           = 
++
++# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
++# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
++# and *.h) to filter out the source-files in the directories. If left 
++# blank all files are included.
++
++EXAMPLE_PATTERNS       = 
++
++# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
++# searched for input files to be used with the \include or \dontinclude 
++# commands irrespective of the value of the RECURSIVE tag. 
++# Possible values are YES and NO. If left blank NO is used.
++
++EXAMPLE_RECURSIVE      = NO
++
++# The IMAGE_PATH tag can be used to specify one or more files or 
++# directories that contain image that are included in the documentation (see 
++# the \image command).
++
++IMAGE_PATH             = 
++
++# The INPUT_FILTER tag can be used to specify a program that doxygen should 
++# invoke to filter for each input file. Doxygen will invoke the filter program 
++# by executing (via popen()) the command <filter> <input-file>, where <filter> 
++# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
++# input file. Doxygen will then use the output that the filter program writes 
++# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
++# ignored.
++
++INPUT_FILTER           = 
++
++# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
++# basis.  Doxygen will compare the file name with each pattern and apply the 
++# filter if there is a match.  The filters are a list of the form: 
++# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
++# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
++# is applied to all files.
++
++FILTER_PATTERNS        = 
++
++# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
++# INPUT_FILTER) will be used to filter the input files when producing source 
++# files to browse (i.e. when SOURCE_BROWSER is set to YES).
++
++FILTER_SOURCE_FILES    = NO
++
++#---------------------------------------------------------------------------
++# configuration options related to source browsing
++#---------------------------------------------------------------------------
++
++# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
++# be generated. Documented entities will be cross-referenced with these sources. 
++# Note: To get rid of all source code in the generated output, make sure also 
++# VERBATIM_HEADERS is set to NO.
++
++SOURCE_BROWSER         = NO
++
++# Setting the INLINE_SOURCES tag to YES will include the body 
++# of functions and classes directly in the documentation.
++
++INLINE_SOURCES         = NO
++
++# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
++# doxygen to hide any special comment blocks from generated source code 
++# fragments. Normal C and C++ comments will always remain visible.
++
++STRIP_CODE_COMMENTS    = YES
++
++# If the REFERENCED_BY_RELATION tag is set to YES 
++# then for each documented function all documented 
++# functions referencing it will be listed.
++
++REFERENCED_BY_RELATION = NO
++
++# If the REFERENCES_RELATION tag is set to YES 
++# then for each documented function all documented entities 
++# called/used by that function will be listed.
++
++REFERENCES_RELATION    = NO
++
++# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
++# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
++# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
++# link to the source code.  Otherwise they will link to the documentstion.
++
++REFERENCES_LINK_SOURCE = YES
++
++# If the USE_HTAGS tag is set to YES then the references to source code 
++# will point to the HTML generated by the htags(1) tool instead of doxygen 
++# built-in source browser. The htags tool is part of GNU's global source 
++# tagging system (see http://www.gnu.org/software/global/global.html). You 
++# will need version 4.8.6 or higher.
++
++USE_HTAGS              = NO
++
++# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
++# will generate a verbatim copy of the header file for each class for 
++# which an include is specified. Set to NO to disable this.
++
++VERBATIM_HEADERS       = YES
++
++#---------------------------------------------------------------------------
++# configuration options related to the alphabetical class index
++#---------------------------------------------------------------------------
++
++# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
++# of all compounds will be generated. Enable this if the project 
++# contains a lot of classes, structs, unions or interfaces.
++
++ALPHABETICAL_INDEX     = NO
++
++# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
++# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
++# in which this list will be split (can be a number in the range [1..20])
++
++COLS_IN_ALPHA_INDEX    = 5
++
++# In case all classes in a project start with a common prefix, all 
++# classes will be put under the same header in the alphabetical index. 
++# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
++# should be ignored while generating the index headers.
++
++IGNORE_PREFIX          = 
++
++#---------------------------------------------------------------------------
++# configuration options related to the HTML output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
++# generate HTML output.
++
++GENERATE_HTML          = YES
++
++# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
++# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
++# put in front of it. If left blank `html' will be used as the default path.
++
++HTML_OUTPUT            = html
++
++# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
++# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
++# doxygen will generate files with .html extension.
++
++HTML_FILE_EXTENSION    = .html
++
++# The HTML_HEADER tag can be used to specify a personal HTML header for 
++# each generated HTML page. If it is left blank doxygen will generate a 
++# standard header.
++
++HTML_HEADER            = 
++
++# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
++# each generated HTML page. If it is left blank doxygen will generate a 
++# standard footer.
++
++HTML_FOOTER            = 
++
++# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
++# style sheet that is used by each HTML page. It can be used to 
++# fine-tune the look of the HTML output. If the tag is left blank doxygen 
++# will generate a default style sheet. Note that doxygen will try to copy 
++# the style sheet file to the HTML output directory, so don't put your own 
++# stylesheet in the HTML output directory as well, or it will be erased!
++
++HTML_STYLESHEET        = 
++
++# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
++# files or namespaces will be aligned in HTML using tables. If set to 
++# NO a bullet list will be used.
++
++HTML_ALIGN_MEMBERS     = YES
++
++# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
++# documentation will contain sections that can be hidden and shown after the 
++# page has loaded. For this to work a browser that supports 
++# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
++# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
++
++HTML_DYNAMIC_SECTIONS  = NO
++
++# If the GENERATE_DOCSET tag is set to YES, additional index files 
++# will be generated that can be used as input for Apple's Xcode 3 
++# integrated development environment, introduced with OSX 10.5 (Leopard). 
++# To create a documentation set, doxygen will generate a Makefile in the 
++# HTML output directory. Running make will produce the docset in that 
++# directory and running "make install" will install the docset in 
++# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
++# it at startup. 
++# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
++
++GENERATE_DOCSET        = NO
++
++# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
++# feed. A documentation feed provides an umbrella under which multiple 
++# documentation sets from a single provider (such as a company or product suite) 
++# can be grouped.
++
++DOCSET_FEEDNAME        = "Doxygen generated docs"
++
++# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
++# should uniquely identify the documentation set bundle. This should be a 
++# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
++# will append .docset to the name.
++
++DOCSET_BUNDLE_ID       = org.doxygen.Project
++
++# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
++# will be generated that can be used as input for tools like the 
++# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
++# of the generated HTML documentation.
++
++GENERATE_HTMLHELP      = NO
++
++# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
++# be used to specify the file name of the resulting .chm file. You 
++# can add a path in front of the file if the result should not be 
++# written to the html output directory.
++
++CHM_FILE               = 
++
++# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
++# be used to specify the location (absolute path including file name) of 
++# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
++# the HTML help compiler on the generated index.hhp.
++
++HHC_LOCATION           = 
++
++# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
++# controls if a separate .chi index file is generated (YES) or that 
++# it should be included in the master .chm file (NO).
++
++GENERATE_CHI           = NO
++
++# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
++# is used to encode HtmlHelp index (hhk), content (hhc) and project file
++# content.
++
++CHM_INDEX_ENCODING     = 
++
++# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
++# controls whether a binary table of contents is generated (YES) or a 
++# normal table of contents (NO) in the .chm file.
++
++BINARY_TOC             = NO
++
++# The TOC_EXPAND flag can be set to YES to add extra items for group members 
++# to the contents of the HTML help documentation and to the tree view.
++
++TOC_EXPAND             = NO
++
++# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER 
++# are set, an additional index file will be generated that can be used as input for 
++# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated 
++# HTML documentation.
++
++GENERATE_QHP           = NO
++
++# If the QHG_LOCATION tag is specified, the QCH_FILE tag can 
++# be used to specify the file name of the resulting .qch file. 
++# The path specified is relative to the HTML output folder.
++
++QCH_FILE               = 
++
++# The QHP_NAMESPACE tag specifies the namespace to use when generating 
++# Qt Help Project output. For more information please see 
++# <a href="http://doc.trolltech.com/qthelpproject.html#namespace">Qt Help Project / Namespace</a>.
++
++QHP_NAMESPACE          = org.doxygen.Project
++
++# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 
++# Qt Help Project output. For more information please see 
++# <a href="http://doc.trolltech.com/qthelpproject.html#virtual-folders">Qt Help Project / Virtual Folders</a>.
++
++QHP_VIRTUAL_FOLDER     = doc
++
++# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 
++# be used to specify the location of Qt's qhelpgenerator. 
++# If non-empty doxygen will try to run qhelpgenerator on the generated 
++# .qhp file .
++
++QHG_LOCATION           = 
++
++# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
++# top of each HTML page. The value NO (the default) enables the index and 
++# the value YES disables it.
++
++DISABLE_INDEX          = NO
++
++# This tag can be used to set the number of enum values (range [1..20]) 
++# that doxygen will group on one line in the generated HTML documentation.
++
++ENUM_VALUES_PER_LINE   = 4
++
++# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
++# structure should be generated to display hierarchical information.
++# If the tag value is set to FRAME, a side panel will be generated
++# containing a tree-like index structure (just like the one that 
++# is generated for HTML Help). For this to work a browser that supports 
++# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
++# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
++# probably better off using the HTML help feature. Other possible values 
++# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
++# and Class Hierarchy pages using a tree view instead of an ordered list;
++# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
++# disables this behavior completely. For backwards compatibility with previous
++# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
++# respectively.
++
++GENERATE_TREEVIEW      = NONE
++
++# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
++# used to set the initial width (in pixels) of the frame in which the tree 
++# is shown.
++
++TREEVIEW_WIDTH         = 250
++
++# Use this tag to change the font size of Latex formulas included 
++# as images in the HTML documentation. The default is 10. Note that 
++# when you change the font size after a successful doxygen run you need 
++# to manually remove any form_*.png images from the HTML output directory 
++# to force them to be regenerated.
++
++FORMULA_FONTSIZE       = 10
++
++#---------------------------------------------------------------------------
++# configuration options related to the LaTeX output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
++# generate Latex output.
++
++GENERATE_LATEX         = NO
++
++# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
++# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
++# put in front of it. If left blank `latex' will be used as the default path.
++
++LATEX_OUTPUT           = latex
++
++# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
++# invoked. If left blank `latex' will be used as the default command name.
++
++LATEX_CMD_NAME         = latex
++
++# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
++# generate index for LaTeX. If left blank `makeindex' will be used as the 
++# default command name.
++
++MAKEINDEX_CMD_NAME     = makeindex
++
++# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
++# LaTeX documents. This may be useful for small projects and may help to 
++# save some trees in general.
++
++COMPACT_LATEX          = NO
++
++# The PAPER_TYPE tag can be used to set the paper type that is used 
++# by the printer. Possible values are: a4, a4wide, letter, legal and 
++# executive. If left blank a4wide will be used.
++
++PAPER_TYPE             = a4wide
++
++# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
++# packages that should be included in the LaTeX output.
++
++EXTRA_PACKAGES         = 
++
++# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
++# the generated latex document. The header should contain everything until 
++# the first chapter. If it is left blank doxygen will generate a 
++# standard header. Notice: only use this tag if you know what you are doing!
++
++LATEX_HEADER           = 
++
++# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
++# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
++# contain links (just like the HTML output) instead of page references 
++# This makes the output suitable for online browsing using a pdf viewer.
++
++PDF_HYPERLINKS         = YES
++
++# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
++# plain latex in the generated Makefile. Set this option to YES to get a 
++# higher quality PDF documentation.
++
++USE_PDFLATEX           = YES
++
++# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
++# command to the generated LaTeX files. This will instruct LaTeX to keep 
++# running if errors occur, instead of asking the user for help. 
++# This option is also used when generating formulas in HTML.
++
++LATEX_BATCHMODE        = NO
++
++# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
++# include the index chapters (such as File Index, Compound Index, etc.) 
++# in the output.
++
++LATEX_HIDE_INDICES     = NO
++
++#---------------------------------------------------------------------------
++# configuration options related to the RTF output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
++# The RTF output is optimized for Word 97 and may not look very pretty with 
++# other RTF readers or editors.
++
++GENERATE_RTF           = NO
++
++# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
++# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
++# put in front of it. If left blank `rtf' will be used as the default path.
++
++RTF_OUTPUT             = rtf
++
++# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
++# RTF documents. This may be useful for small projects and may help to 
++# save some trees in general.
++
++COMPACT_RTF            = NO
++
++# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
++# will contain hyperlink fields. The RTF file will 
++# contain links (just like the HTML output) instead of page references. 
++# This makes the output suitable for online browsing using WORD or other 
++# programs which support those fields. 
++# Note: wordpad (write) and others do not support links.
++
++RTF_HYPERLINKS         = NO
++
++# Load stylesheet definitions from file. Syntax is similar to doxygen's 
++# config file, i.e. a series of assignments. You only have to provide 
++# replacements, missing definitions are set to their default value.
++
++RTF_STYLESHEET_FILE    = 
++
++# Set optional variables used in the generation of an rtf document. 
++# Syntax is similar to doxygen's config file.
++
++RTF_EXTENSIONS_FILE    = 
++
++#---------------------------------------------------------------------------
++# configuration options related to the man page output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
++# generate man pages
++
++GENERATE_MAN           = NO
++
++# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
++# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
++# put in front of it. If left blank `man' will be used as the default path.
++
++MAN_OUTPUT             = man
++
++# The MAN_EXTENSION tag determines the extension that is added to 
++# the generated man pages (default is the subroutine's section .3)
++
++MAN_EXTENSION          = .3
++
++# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
++# then it will generate one additional man file for each entity 
++# documented in the real man page(s). These additional files 
++# only source the real man page, but without them the man command 
++# would be unable to find the correct page. The default is NO.
++
++MAN_LINKS              = NO
++
++#---------------------------------------------------------------------------
++# configuration options related to the XML output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_XML tag is set to YES Doxygen will 
++# generate an XML file that captures the structure of 
++# the code including all documentation.
++
++GENERATE_XML           = NO
++
++# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
++# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
++# put in front of it. If left blank `xml' will be used as the default path.
++
++XML_OUTPUT             = xml
++
++# The XML_SCHEMA tag can be used to specify an XML schema, 
++# which can be used by a validating XML parser to check the 
++# syntax of the XML files.
++
++XML_SCHEMA             = 
++
++# The XML_DTD tag can be used to specify an XML DTD, 
++# which can be used by a validating XML parser to check the 
++# syntax of the XML files.
++
++XML_DTD                = 
++
++# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
++# dump the program listings (including syntax highlighting 
++# and cross-referencing information) to the XML output. Note that 
++# enabling this will significantly increase the size of the XML output.
++
++XML_PROGRAMLISTING     = YES
++
++#---------------------------------------------------------------------------
++# configuration options for the AutoGen Definitions output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
++# generate an AutoGen Definitions (see autogen.sf.net) file 
++# that captures the structure of the code including all 
++# documentation. Note that this feature is still experimental 
++# and incomplete at the moment.
++
++GENERATE_AUTOGEN_DEF   = NO
++
++#---------------------------------------------------------------------------
++# configuration options related to the Perl module output
++#---------------------------------------------------------------------------
++
++# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
++# generate a Perl module file that captures the structure of 
++# the code including all documentation. Note that this 
++# feature is still experimental and incomplete at the 
++# moment.
++
++GENERATE_PERLMOD       = NO
++
++# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
++# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
++# to generate PDF and DVI output from the Perl module output.
++
++PERLMOD_LATEX          = NO
++
++# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
++# nicely formatted so it can be parsed by a human reader.  This is useful 
++# if you want to understand what is going on.  On the other hand, if this 
++# tag is set to NO the size of the Perl module output will be much smaller 
++# and Perl will parse it just the same.
++
++PERLMOD_PRETTY         = YES
++
++# The names of the make variables in the generated doxyrules.make file 
++# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
++# This is useful so different doxyrules.make files included by the same 
++# Makefile don't overwrite each other's variables.
++
++PERLMOD_MAKEVAR_PREFIX = 
++
++#---------------------------------------------------------------------------
++# Configuration options related to the preprocessor   
++#---------------------------------------------------------------------------
++
++# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
++# evaluate all C-preprocessor directives found in the sources and include 
++# files.
++
++ENABLE_PREPROCESSING   = YES
++
++# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
++# names in the source code. If set to NO (the default) only conditional 
++# compilation will be performed. Macro expansion can be done in a controlled 
++# way by setting EXPAND_ONLY_PREDEF to YES.
++
++MACRO_EXPANSION        = NO
++
++# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
++# then the macro expansion is limited to the macros specified with the 
++# PREDEFINED and EXPAND_AS_DEFINED tags.
++
++EXPAND_ONLY_PREDEF     = NO
++
++# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
++# in the INCLUDE_PATH (see below) will be search if a #include is found.
++
++SEARCH_INCLUDES        = YES
++
++# The INCLUDE_PATH tag can be used to specify one or more directories that 
++# contain include files that are not input files but should be processed by 
++# the preprocessor.
++
++INCLUDE_PATH           = 
++
++# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
++# patterns (like *.h and *.hpp) to filter out the header-files in the 
++# directories. If left blank, the patterns specified with FILE_PATTERNS will 
++# be used.
++
++INCLUDE_FILE_PATTERNS  = 
++
++# The PREDEFINED tag can be used to specify one or more macro names that 
++# are defined before the preprocessor is started (similar to the -D option of 
++# gcc). The argument of the tag is a list of macros of the form: name 
++# or name=definition (no spaces). If the definition and the = are 
++# omitted =1 is assumed. To prevent a macro definition from being 
++# undefined via #undef or recursively expanded use the := operator 
++# instead of the = operator.
++
++PREDEFINED             = 
++
++# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
++# this tag can be used to specify a list of macro names that should be expanded. 
++# The macro definition that is found in the sources will be used. 
++# Use the PREDEFINED tag if you want to use a different macro definition.
++
++EXPAND_AS_DEFINED      = 
++
++# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
++# doxygen's preprocessor will remove all function-like macros that are alone 
++# on a line, have an all uppercase name, and do not end with a semicolon. Such 
++# function macros are typically used for boiler-plate code, and will confuse 
++# the parser if not removed.
++
++SKIP_FUNCTION_MACROS   = YES
++
++#---------------------------------------------------------------------------
++# Configuration::additions related to external references   
++#---------------------------------------------------------------------------
++
++# The TAGFILES option can be used to specify one or more tagfiles. 
++# Optionally an initial location of the external documentation 
++# can be added for each tagfile. The format of a tag file without 
++# this location is as follows: 
++#   TAGFILES = file1 file2 ... 
++# Adding location for the tag files is done as follows: 
++#   TAGFILES = file1=loc1 "file2 = loc2" ... 
++# where "loc1" and "loc2" can be relative or absolute paths or 
++# URLs. If a location is present for each tag, the installdox tool 
++# does not have to be run to correct the links.
++# Note that each tag file must have a unique name
++# (where the name does NOT include the path)
++# If a tag file is not located in the directory in which doxygen 
++# is run, you must also specify the path to the tagfile here.
++
++TAGFILES               = 
++
++# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
++# a tag file that is based on the input files it reads.
++
++GENERATE_TAGFILE       = 
++
++# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
++# in the class index. If set to NO only the inherited external classes 
++# will be listed.
++
++ALLEXTERNALS           = NO
++
++# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
++# in the modules index. If set to NO, only the current project's groups will 
++# be listed.
++
++EXTERNAL_GROUPS        = YES
++
++# The PERL_PATH should be the absolute path and name of the perl script 
++# interpreter (i.e. the result of `which perl').
++
++PERL_PATH              = /usr/bin/perl
++
++#---------------------------------------------------------------------------
++# Configuration options related to the dot tool   
++#---------------------------------------------------------------------------
++
++# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
++# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
++# or super classes. Setting the tag to NO turns the diagrams off. Note that 
++# this option is superseded by the HAVE_DOT option below. This is only a 
++# fallback. It is recommended to install and use dot, since it yields more 
++# powerful graphs.
++
++CLASS_DIAGRAMS         = YES
++
++# You can define message sequence charts within doxygen comments using the \msc 
++# command. Doxygen will then run the mscgen tool (see 
++# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
++# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
++# the mscgen tool resides. If left empty the tool is assumed to be found in the 
++# default search path.
++
++MSCGEN_PATH            = 
++
++# If set to YES, the inheritance and collaboration graphs will hide 
++# inheritance and usage relations if the target is undocumented 
++# or is not a class.
++
++HIDE_UNDOC_RELATIONS   = YES
++
++# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
++# available from the path. This tool is part of Graphviz, a graph visualization 
++# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
++# have no effect if this option is set to NO (the default)
++
++HAVE_DOT               = NO
++
++# By default doxygen will write a font called FreeSans.ttf to the output 
++# directory and reference it in all dot files that doxygen generates. This 
++# font does not include all possible unicode characters however, so when you need 
++# these (or just want a differently looking font) you can specify the font name 
++# using DOT_FONTNAME. You need need to make sure dot is able to find the font, 
++# which can be done by putting it in a standard location or by setting the 
++# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory 
++# containing the font.
++
++DOT_FONTNAME           = FreeSans
++
++# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 
++# The default size is 10pt.
++
++DOT_FONTSIZE           = 10
++
++# By default doxygen will tell dot to use the output directory to look for the 
++# FreeSans.ttf font (which doxygen will put there itself). If you specify a 
++# different font using DOT_FONTNAME you can set the path where dot 
++# can find it using this tag.
++
++DOT_FONTPATH           = 
++
++# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
++# will generate a graph for each documented class showing the direct and 
++# indirect inheritance relations. Setting this tag to YES will force the 
++# the CLASS_DIAGRAMS tag to NO.
++
++CLASS_GRAPH            = YES
++
++# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
++# will generate a graph for each documented class showing the direct and 
++# indirect implementation dependencies (inheritance, containment, and 
++# class references variables) of the class with other documented classes.
++
++COLLABORATION_GRAPH    = YES
++
++# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
++# will generate a graph for groups, showing the direct groups dependencies
++
++GROUP_GRAPHS           = YES
++
++# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
++# collaboration diagrams in a style similar to the OMG's Unified Modeling 
++# Language.
++
++UML_LOOK               = NO
++
++# If set to YES, the inheritance and collaboration graphs will show the 
++# relations between templates and their instances.
++
++TEMPLATE_RELATIONS     = NO
++
++# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
++# tags are set to YES then doxygen will generate a graph for each documented 
++# file showing the direct and indirect include dependencies of the file with 
++# other documented files.
++
++INCLUDE_GRAPH          = YES
++
++# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
++# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
++# documented header file showing the documented files that directly or 
++# indirectly include this file.
++
++INCLUDED_BY_GRAPH      = YES
++
++# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
++# doxygen will generate a call dependency graph for every global function 
++# or class method. Note that enabling this option will significantly increase 
++# the time of a run. So in most cases it will be better to enable call graphs 
++# for selected functions only using the \callgraph command.
++
++CALL_GRAPH             = NO
++
++# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
++# doxygen will generate a caller dependency graph for every global function 
++# or class method. Note that enabling this option will significantly increase 
++# the time of a run. So in most cases it will be better to enable caller 
++# graphs for selected functions only using the \callergraph command.
++
++CALLER_GRAPH           = NO
++
++# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
++# will graphical hierarchy of all classes instead of a textual one.
++
++GRAPHICAL_HIERARCHY    = YES
++
++# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
++# then doxygen will show the dependencies a directory has on other directories 
++# in a graphical way. The dependency relations are determined by the #include
++# relations between the files in the directories.
++
++DIRECTORY_GRAPH        = YES
++
++# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
++# generated by dot. Possible values are png, jpg, or gif
++# If left blank png will be used.
++
++DOT_IMAGE_FORMAT       = png
++
++# The tag DOT_PATH can be used to specify the path where the dot tool can be 
++# found. If left blank, it is assumed the dot tool can be found in the path.
++
++DOT_PATH               = 
++
++# The DOTFILE_DIRS tag can be used to specify one or more directories that 
++# contain dot files that are included in the documentation (see the 
++# \dotfile command).
++
++DOTFILE_DIRS           = 
++
++# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
++# nodes that will be shown in the graph. If the number of nodes in a graph 
++# becomes larger than this value, doxygen will truncate the graph, which is 
++# visualized by representing a node as a red box. Note that doxygen if the 
++# number of direct children of the root node in a graph is already larger than 
++# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
++# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
++
++DOT_GRAPH_MAX_NODES    = 50
++
++# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
++# graphs generated by dot. A depth value of 3 means that only nodes reachable 
++# from the root by following a path via at most 3 edges will be shown. Nodes 
++# that lay further from the root node will be omitted. Note that setting this 
++# option to 1 or 2 may greatly reduce the computation time needed for large 
++# code bases. Also note that the size of a graph can be further restricted by 
++# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
++
++MAX_DOT_GRAPH_DEPTH    = 0
++
++# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
++# background. This is disabled by default, because dot on Windows does not 
++# seem to support this out of the box. Warning: Depending on the platform used, 
++# enabling this option may lead to badly anti-aliased labels on the edges of 
++# a graph (i.e. they become hard to read).
++
++DOT_TRANSPARENT        = NO
++
++# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
++# files in one run (i.e. multiple -o and -T options on the command line). This 
++# makes dot run faster, but since only newer versions of dot (>1.8.10) 
++# support this, this feature is disabled by default.
++
++DOT_MULTI_TARGETS      = NO
++
++# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
++# generate a legend page explaining the meaning of the various boxes and 
++# arrows in the dot generated graphs.
++
++GENERATE_LEGEND        = YES
++
++# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
++# remove the intermediate dot files that are used to generate 
++# the various graphs.
++
++DOT_CLEANUP            = YES
++
++#---------------------------------------------------------------------------
++# Configuration::additions related to the search engine   
++#---------------------------------------------------------------------------
++
++# The SEARCHENGINE tag specifies whether or not a search engine should be 
++# used. If set to NO the values of all tags below this one will be ignored.
++
++SEARCHENGINE           = NO
+diff --git a/libiscsi/libiscsi.h b/libiscsi/libiscsi.h
+new file mode 100644
+index 000000000000..756590e14d8b
+--- /dev/null
++++ b/libiscsi/libiscsi.h
+@@ -0,0 +1,344 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#ifndef __LIBISCSI_H
++#define __LIBISCSI_H
++
++#include <netdb.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif /* __cplusplus */
++
++#if __GNUC__ >= 4
++#define PUBLIC __attribute__ ((visibility("default")))
++#else
++#define PUBLIC
++#endif
++
++/** \brief Maximum length for iSCSI values.
++ *
++ * Maximum length for iSCSI values such as hostnames and parameter values.
++ */
++#define LIBISCSI_VALUE_MAXLEN 256
++
++/** \brief supported authentication methods
++ *
++ * This enum lists all supported authentication methods.
++ */
++enum libiscsi_auth_t {
++    libiscsi_auth_none   /** No authentication */,
++    libiscsi_auth_chap   /** CHAP authentication */,
++};
++
++/** \brief libiscsi context struct
++ *
++ * Note: even though libiscsi uses a context struct, the underlying open-iscsi
++ * code does not, so libiscsi is not thread safe, not even when using one
++ * context per thread!
++ */
++struct libiscsi_context;
++
++/** \brief iSCSI node record
++ *
++ * Struct holding data uniquely identifying an iSCSI node.
++ */
++struct libiscsi_node {
++    char name[LIBISCSI_VALUE_MAXLEN]     /** iSCSI iqn for the node. */;
++    int tpgt                             /** Portal group number. */;
++    /* Note open-iscsi has some code in place for multiple connections in one
++       node record and thus multiple address / port combi's, but this does not
++       get used anywhere, so we keep things simple and assume one connection */
++    char address[NI_MAXHOST]             /** Portal hostname or IP-address. */;
++    int port                             /** Portal port number. */;
++    char iface[LIBISCSI_VALUE_MAXLEN]    /** Interface to connect through. */;
++};
++
++/** \brief libiscsi CHAP authentication information struct
++ *
++ * Struct holding all data needed for CHAP login / authentication. Note that
++ * \e reverse_username may be a 0 length string in which case only forward
++ * authentication will be done.
++ */
++struct libiscsi_chap_auth_info {
++    char username[LIBISCSI_VALUE_MAXLEN]         /** Username */;
++    char password[LIBISCSI_VALUE_MAXLEN]         /** Password */;
++    char reverse_username[LIBISCSI_VALUE_MAXLEN] /** Reverse Username */;
++    char reverse_password[LIBISCSI_VALUE_MAXLEN] /** Reverse Password */;
++};
++
++/** \brief generic libiscsi authentication information struct
++ *
++ * Struct holding authentication information for discovery and login.
++ */
++struct libiscsi_auth_info {
++    enum libiscsi_auth_t method /** Authentication method to use */;
++    union {
++        struct libiscsi_chap_auth_info chap /** Chap specific info */;
++    } /** Union holding method depenend info */;
++};
++
++/** \brief Initalize libiscsi
++ *
++ * This function creates a libiscsi context and initalizes it. This context
++ * is need to use other libiscsi funtions.
++ *
++ * \return     A pointer to the created context, or NULL in case of an error.
++ */
++PUBLIC struct libiscsi_context *libiscsi_init(void);
++
++/** \brief Cleanup libiscsi used resource
++ *
++ * This function cleanups any used resources and then destroys the passed
++ * context. After this the passed in context may no longer be used!
++ *
++ * \param context                libiscsi context to operate on.
++ */
++PUBLIC void libiscsi_cleanup(struct libiscsi_context *context);
++
++/** \brief Discover iSCSI nodes using sendtargets and add them to the node db.
++ *
++ * This function connects to the given address and port and then tries to
++ * discover iSCSI nodes using the sendtargets protocol. Any found nodes are
++ * added to the local iSCSI node database and are returned in a dynamically
++ * allocated array.
++ *
++ * Note that the (optional) authentication info is for authenticating the
++ * discovery, and is not for the found nodes! If the connection(s) to the
++ * node(s) need authentication too, you can set the username / password for
++ * those (which can be different!) using the libiscsi_node_set_auth() function.
++ *
++ * \param context                libiscsi context to operate on.
++ * \param address                Hostname or IP-address to connect to.
++ * \param port                   Port to connect to, or 0 for the default port.
++ * \param auth_info              Authentication information, or NULL.
++ * \param nr_found		 The number of found nodes will be returned
++ *                               through this pointer if not NULL.
++ * \param found_nodes            The address of the dynamically allocated array
++ *                               of found nodes will be returned through this
++ *                               pointer if not NULL. The caller must free this
++ *                               array using free().
++ * \return                       0 on success, otherwise a standard error code
++ *                               (from errno.h).
++ */
++PUBLIC int libiscsi_discover_sendtargets(struct libiscsi_context *context,
++    const char *address, int port, const struct libiscsi_auth_info *auth_info,
++    int *nr_found, struct libiscsi_node **found_nodes);
++
++/** \brief Read iSCSI node info from firmware and add them to the node db.
++ *
++ * This function discovers iSCSI nodes using firmware (ppc or ibft). Any found
++ * nodes are added to the local iSCSI node database and are returned in a
++ * dynamically allocated array.
++ *
++ * Note that unlike sendtargets discovery, this function will also read
++ * authentication info and store that in the database too.
++ *
++ * Note this function currently is a stub which will always return -EINVAL
++ * (IOW it is not yet implemented)
++ *
++ * \param context                libiscsi context to operate on.
++ * \param nr_found		 The number of found nodes will be returned
++ *                               through this pointer if not NULL.
++ * \param found_nodes            The address of the dynamically allocated array
++ *                               of found nodes will be returned through this
++ *                               pointer if not NULL. The caller must free this
++ *                               array using free().
++ * \return                       0 on success, otherwise a standard error code
++ *                               (from errno.h).
++ */
++PUBLIC int libiscsi_discover_firmware(struct libiscsi_context *context,
++    int *nr_found, struct libiscsi_node **found_nodes);
++
++/** \brief Check validity of the given authentication info.
++ *
++ * This function checks the validity of the given authentication info. For 
++ * example in case of CHAP, if the username and password are not empty.
++ *
++ * This function is mainly intended for use by language bindings.
++ *
++ * \param context                libiscsi context to operate on.
++ * \param auth_info              Authentication information to check.
++ * \return                       0 on success, otherwise EINVAL.
++ */
++PUBLIC int libiscsi_verify_auth_info(struct libiscsi_context *context,
++	const struct libiscsi_auth_info *auth_info);
++
++/** \brief Set the authentication info for the given node.
++ *
++ * This function sets the authentication information for the node described by
++ * the given node record. This will overwrite any existing authentication
++ * information.
++ *
++ * This is the way to specify authentication information for nodes found
++ * through sendtargets discovery.
++ *
++ * Note:
++ * 1) This is a convience wrapper around libiscsi_node_set_parameter(),
++ *    setting the node.session.auth.* parameters.
++ * 2) For nodes found through firmware discovery the authentication information
++ *    has already been set from the firmware.
++ * 3) \e auth_info may be NULL in which case any existing authinfo will be
++ *    cleared.
++ *
++ * \param context                libiscsi context to operate on.
++ * \param node                   iSCSI node to set auth information of
++ * \param auth_info              Authentication information, or NULL.
++ * \return                       0 on success, otherwise a standard error code
++ *                               (from errno.h).
++ */
++PUBLIC int libiscsi_node_set_auth(struct libiscsi_context *context,
++    const struct libiscsi_node *node,
++    const struct libiscsi_auth_info *auth_info);
++
++/** \brief Get the authentication info for the given node.
++ *
++ * This function gets the authentication information for the node described by
++ * the given node record.
++ *
++ * \param context                libiscsi context to operate on.
++ * \param node                   iSCSI node to set auth information of
++ * \param auth_info              Pointer to a libiscsi_auth_info struct where
++ *                               the retreived information will be stored.
++ * \return                       0 on success, otherwise a standard error code
++ *                               (from errno.h).
++ */
++PUBLIC int libiscsi_node_get_auth(struct libiscsi_context *context,
++    const struct libiscsi_node *node,
++    struct libiscsi_auth_info *auth_info);
++
++/** \brief Login to an iSCSI node.
++ *
++ * Login to the iSCSI node described by the given node record.
++ *
++ * \param context       libiscsi context to operate on.
++ * \param node          iSCSI node to login to.
++ * \return              0 on success, otherwise a standard error code
++ *                      (from errno.h).
++ */
++PUBLIC int libiscsi_node_login(struct libiscsi_context *context,
++    const struct libiscsi_node *node);
++
++/** \brief Logout of an iSCSI node.
++ *
++ * Logout of the iSCSI node described by the given node record.
++ *
++ * \param context       libiscsi context to operate on.
++ * \param node          iSCSI node to logout from.
++ * \return              0 on success, otherwise a standard error code
++ *                      (from errno.h).
++ */
++PUBLIC int libiscsi_node_logout(struct libiscsi_context *context,
++    const struct libiscsi_node *node);
++
++/** \brief Set an iSCSI parameter for the given node
++ *
++ * Set the given nodes iSCSI parameter named by \e parameter to value \e value.
++ *
++ * \param context       libiscsi context to operate on.
++ * \param node          iSCSI node to change a parameter from.
++ * \param parameter     Name of the parameter to set.
++ * \param value         Value to set the parameter too.
++ * \return              0 on success, otherwise a standard error code
++ *                      (from errno.h).
++ */
++PUBLIC int libiscsi_node_set_parameter(struct libiscsi_context *context,
++    const struct libiscsi_node *node,
++    const char *parameter, const char *value);
++
++/** \brief Get the value of an iSCSI parameter for the given node
++ *
++ * Get the value of the given nodes iSCSI parameter named by \e parameter.
++ *
++ * \param context       libiscsi context to operate on.
++ * \param node          iSCSI node to change a parameter from.
++ * \param parameter     Name of the parameter to get.
++ * \param value         The retreived value is stored here, this buffer must be
++ *                      atleast LIBISCSI_VALUE_MAXLEN bytes large.
++ * \return              0 on success, otherwise a standard error code
++ *                      (from errno.h).
++ */
++PUBLIC int libiscsi_node_get_parameter(struct libiscsi_context *context,
++    const struct libiscsi_node *node, const char *parameter, char *value);
++
++/** \brief Get human readable string describing the last libiscsi error.
++ *
++ * This function can be called to get a human readable error string when a
++ * libiscsi function has returned an error. This function uses a single buffer
++ * per context, thus the result is only valid as long as no other libiscsi
++ * calls are made on the same context after the failing function call.
++ *
++ * \param context       libiscsi context to operate on.
++ *
++ * \return human readable string describing the last libiscsi error.
++ */
++PUBLIC const char *libiscsi_get_error_string(struct libiscsi_context *context);
++
++
++/************************** Utility functions *******************************/
++
++/** \brief libiscsi network config struct
++ *
++ * libiscsi network config struct.
++ */
++struct libiscsi_network_config {
++    int dhcp                                  /** Using DHCP? (boolean). */;
++    char iface_name[LIBISCSI_VALUE_MAXLEN]    /** Interface name. */;
++    char mac_address[LIBISCSI_VALUE_MAXLEN]   /** MAC address. */;
++    char ip_address[LIBISCSI_VALUE_MAXLEN]    /** IP address. */;
++    char netmask[LIBISCSI_VALUE_MAXLEN]       /** Netmask. */;
++    char gateway[LIBISCSI_VALUE_MAXLEN]       /** IP of Default gateway. */;
++    char primary_dns[LIBISCSI_VALUE_MAXLEN]   /** IP of the Primary DNS. */;
++    char secondary_dns[LIBISCSI_VALUE_MAXLEN] /** IP of the Secondary DNS. */;
++};
++
++/** \brief Get network configuration information from iscsi firmware
++ *
++ * Function can be called to get the network configuration information
++ * (like dhcp, ip, netmask, default gateway, etc.) from the firmware of a
++ * network adapter with iscsi boot firmware.
++ *
++ * Note that not all fields of the returned struct are necessarilly filled,
++ * unset fields contain a 0 length string.
++ *
++ * \param config        pointer to a libiscsi_network_config struct to fill.
++ *
++ * \return              0 on success, ENODEV when no iscsi firmware was found.
++ */
++PUBLIC int libiscsi_get_firmware_network_config(
++    struct libiscsi_network_config *config);
++
++/** \brief Get the initiator name (iqn) from the iscsi firmware
++ *
++ * Get the initiator name (iqn) from the iscsi firmware.
++ *
++ * \param initiatorname The initiator name is stored here, this buffer must be
++ *                      atleast LIBISCSI_VALUE_MAXLEN bytes large.
++ * \return              0 on success, ENODEV when no iscsi firmware was found.
++ */
++PUBLIC int libiscsi_get_firmware_initiator_name(char *initiatorname);
++
++#undef PUBLIC
++
++#ifdef __cplusplus
++}
++#endif /* __cplusplus */
++
++#endif
+diff --git a/libiscsi/pylibiscsi.c b/libiscsi/pylibiscsi.c
+new file mode 100644
+index 000000000000..4b09aa7b956a
+--- /dev/null
++++ b/libiscsi/pylibiscsi.c
+@@ -0,0 +1,638 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <Python.h>
++#include "libiscsi.h"
++
++static struct libiscsi_context *context = NULL;
++
++/****************************** helpers ***********************************/
++static int check_string(const char *string)
++{
++	if (strlen(string) >= LIBISCSI_VALUE_MAXLEN) {
++		PyErr_SetString(PyExc_ValueError, "string too long");
++		return -1;
++	}
++	return 0;
++}
++
++/********************** PyIscsiChapAuthInfo ***************************/
++
++typedef struct {
++	PyObject_HEAD
++
++	struct libiscsi_auth_info info;
++} PyIscsiChapAuthInfo;
++
++static int PyIscsiChapAuthInfo_init(PyObject *self, PyObject *args,
++				    PyObject *kwds)
++{
++	int i;
++	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
++	char *kwlist[] = {"username", "password", "reverse_username",
++				"reverse_password", NULL};
++	const char *string[4] = { NULL, NULL, NULL, NULL };
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds,
++					"zz|zz:chapAuthInfo.__init__",
++					kwlist, &string[0], &string[1],
++					&string[2], &string[3]))
++		return -1;
++
++	for (i = 0; i < 4; i++)
++		if (string[i] && check_string(string[i]))
++			return -1;
++
++	memset (&chap->info, 0, sizeof(chap->info));
++	chap->info.method = libiscsi_auth_chap;
++	if (string[0])
++		strcpy(chap->info.chap.username, string[0]);
++	if (string[1])
++		strcpy(chap->info.chap.password, string[1]);
++	if (string[2])
++		strcpy(chap->info.chap.reverse_username, string[2]);
++	if (string[3])
++		strcpy(chap->info.chap.reverse_password, string[3]);
++
++	if (libiscsi_verify_auth_info(context, &chap->info)) {
++		PyErr_SetString(PyExc_ValueError,
++				libiscsi_get_error_string(context));
++		return -1;
++	}
++	return 0;
++}
++
++static PyObject *PyIscsiChapAuthInfo_get(PyObject *self, void *data)
++{
++	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
++	const char *attr = (const char *)data;
++
++	if (!strcmp(attr, "username")) {
++		return PyString_FromString(chap->info.chap.username);
++	} else if (!strcmp(attr, "password")) {
++		return PyString_FromString(chap->info.chap.password);
++	} else if (!strcmp(attr, "reverse_username")) {
++		return PyString_FromString(chap->info.chap.reverse_username);
++	} else if (!strcmp(attr, "reverse_password")) {
++		return PyString_FromString(chap->info.chap.reverse_password);
++	}
++	return NULL;
++}
++
++static int PyIscsiChapAuthInfo_set(PyObject *self, PyObject *value, void *data)
++{
++	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
++	const char *attr = (const char *)data;
++	const char *str;
++
++	if (!PyArg_Parse(value, "s", &str) || check_string(str))
++		return -1;
++
++	if (!strcmp(attr, "username")) {
++		strcpy(chap->info.chap.username, str);
++	} else if (!strcmp(attr, "password")) {
++		strcpy(chap->info.chap.password, str);
++	} else if (!strcmp(attr, "reverse_username")) {
++		strcpy(chap->info.chap.reverse_username, str);
++	} else if (!strcmp(attr, "reverse_password")) {
++		strcpy(chap->info.chap.reverse_password, str);
++	}
++
++	return 0;
++}
++
++static int PyIscsiChapAuthInfo_compare(PyIscsiChapAuthInfo *self,
++				       PyIscsiChapAuthInfo *other)
++{
++	int r;
++
++	r = strcmp(self->info.chap.username, other->info.chap.username);
++	if (r)
++		return r;
++
++	r = strcmp(self->info.chap.password, other->info.chap.password);
++	if (r)
++		return r;
++
++	r = strcmp(self->info.chap.reverse_username,
++		   other->info.chap.reverse_username);
++	if (r)
++		return r;
++
++	r = strcmp(self->info.chap.reverse_password,
++		   other->info.chap.reverse_password);
++	return r;
++}
++
++static PyObject *PyIscsiChapAuthInfo_str(PyObject *self)
++{
++	PyIscsiChapAuthInfo *chap = (PyIscsiChapAuthInfo *)self;
++	char s[1024], reverse[512] = "";
++
++	if (chap->info.chap.reverse_username[0])
++		snprintf(reverse, sizeof(reverse), ", %s:%s",
++			 chap->info.chap.reverse_username,
++			 chap->info.chap.reverse_password);
++
++	snprintf(s, sizeof(s), "%s:%s%s", chap->info.chap.username,
++		 chap->info.chap.password, reverse);
++
++	return PyString_FromString(s);
++}
++
++static struct PyGetSetDef PyIscsiChapAuthInfo_getseters[] = {
++	{"username", (getter)PyIscsiChapAuthInfo_get,
++		(setter)PyIscsiChapAuthInfo_set,
++		"username", "username"},
++	{"password", (getter)PyIscsiChapAuthInfo_get,
++		(setter)PyIscsiChapAuthInfo_set,
++		"password", "password"},
++	{"reverse_username", (getter)PyIscsiChapAuthInfo_get,
++		(setter)PyIscsiChapAuthInfo_set,
++		"reverse_username", "reverse_username"},
++	{"reverse_password", (getter)PyIscsiChapAuthInfo_get,
++		(setter)PyIscsiChapAuthInfo_set,
++		"reverse_password", "reverse_password"},
++	{NULL}
++};
++
++PyTypeObject PyIscsiChapAuthInfo_Type = {
++	PyObject_HEAD_INIT(NULL)
++	.tp_name = "libiscsi.chapAuthInfo",
++	.tp_basicsize = sizeof (PyIscsiChapAuthInfo),
++	.tp_getset = PyIscsiChapAuthInfo_getseters,
++	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
++		    Py_TPFLAGS_BASETYPE,
++	.tp_compare = (cmpfunc)PyIscsiChapAuthInfo_compare,
++	.tp_init = PyIscsiChapAuthInfo_init,
++	.tp_str = PyIscsiChapAuthInfo_str,
++	.tp_new = PyType_GenericNew,
++	.tp_doc = "iscsi chap authentication information.",
++};
++
++/***************************** PyIscsiNode  ********************************/
++
++typedef struct {
++	PyObject_HEAD
++
++	struct libiscsi_node node;
++} PyIscsiNode;
++
++static int PyIscsiNode_init(PyObject *self, PyObject *args, PyObject *kwds)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	char *kwlist[] = {"name", "tpgt", "address", "port", "iface", NULL};
++	const char *name = NULL, *address = NULL, *iface = NULL;
++	int tpgt = -1, port = 3260;
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|isis:node.__init__",
++					 kwlist, &name, &tpgt, &address,
++					 &port, &iface))
++		return -1;
++	if (address == NULL) {
++		PyErr_SetString(PyExc_ValueError, "address not set");
++		return -1;
++	}
++	if (check_string(name) || check_string(address) || check_string(iface))
++		return -1;
++
++	strcpy(node->node.name, name);
++	node->node.tpgt = tpgt;
++	strcpy(node->node.address, address);
++	node->node.port = port;
++	strcpy(node->node.iface, iface);
++
++	return 0;
++}
++
++static PyObject *PyIscsiNode_get(PyObject *self, void *data)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	const char *attr = (const char *)data;
++
++	if (!strcmp(attr, "name")) {
++		return PyString_FromString(node->node.name);
++	} else if (!strcmp(attr, "tpgt")) {
++		return PyInt_FromLong(node->node.tpgt);
++	} else if (!strcmp(attr, "address")) {
++		return PyString_FromString(node->node.address);
++	} else if (!strcmp(attr, "port")) {
++		return PyInt_FromLong(node->node.port);
++	} else if (!strcmp(attr, "iface")) {
++		return PyString_FromString(node->node.iface);
++	}
++	return NULL;
++}
++
++static int PyIscsiNode_set(PyObject *self, PyObject *value, void *data)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	const char *attr = (const char *)data;
++	const char *str;
++	int i;
++
++	if (!strcmp(attr, "name")) {
++		if (!PyArg_Parse(value, "s", &str) || check_string(str))
++			return -1;
++		strcpy(node->node.name, str);
++	} else if (!strcmp(attr, "tpgt")) {
++		if (!PyArg_Parse(value, "i", &i))
++			return -1;
++		node->node.tpgt = i;
++	} else if (!strcmp(attr, "address")) {
++		if (!PyArg_Parse(value, "s", &str) || check_string(str))
++			return -1;
++		strcpy(node->node.address, str);
++	} else if (!strcmp(attr, "port")) {
++		if (!PyArg_Parse(value, "i", &i))
++			return -1;
++		node->node.port = i;
++	} else if (!strcmp(attr, "iface")) {
++		if (!PyArg_Parse(value, "s", &str) || check_string(str))
++			return -1;
++		strcpy(node->node.iface, str);
++	}
++
++	return 0;
++}
++
++static int PyIscsiNode_compare(PyIscsiNode *self, PyIscsiNode *other)
++{
++	int res;
++
++	res = strcmp(self->node.name, other->node.name);
++	if (res)
++		return res;
++
++	if (self->node.tpgt < other->node.tpgt)
++		return -1;
++	if (self->node.tpgt > other->node.tpgt)
++		return -1;
++
++	res = strcmp(self->node.address, other->node.address);
++	if (res)
++		return res;
++
++	if (self->node.port < other->node.port)
++		return -1;
++	if (self->node.port > other->node.port)
++		return -1;
++
++	res = strcmp(self->node.iface, other->node.iface);
++	if (res)
++		return res;
++
++	return 0;
++}
++
++static PyObject *PyIscsiNode_str(PyObject *self)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	char s[1024], tpgt[16] = "";
++
++	if (node->node.tpgt != -1)
++		sprintf(tpgt, ",%d", node->node.tpgt);
++
++	snprintf(s, sizeof(s), "%s:%d%s %s", node->node.address,
++		 node->node.port, tpgt, node->node.name);
++
++	return PyString_FromString(s);
++}
++
++static PyObject *PyIscsiNode_login(PyObject *self)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++
++	if (libiscsi_node_login(context, &node->node)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++	Py_RETURN_NONE;
++}
++
++static PyObject *PyIscsiNode_logout(PyObject *self)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++
++	if (libiscsi_node_logout(context, &node->node)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++	Py_RETURN_NONE;
++}
++
++static PyObject *PyIscsiNode_setAuth(PyObject *self, PyObject *args,
++				     PyObject *kwds)
++{
++	char *kwlist[] = {"authinfo", NULL};
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	PyObject *arg;
++	const struct libiscsi_auth_info *authinfo = NULL;
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &arg))
++		return NULL;
++
++	if (arg == Py_None) {
++		authinfo = NULL;
++	} else if (PyObject_IsInstance(arg, (PyObject *)
++				       &PyIscsiChapAuthInfo_Type)) {
++		PyIscsiChapAuthInfo *pyauthinfo = (PyIscsiChapAuthInfo *)arg;
++		authinfo = &pyauthinfo->info;
++	} else {
++		PyErr_SetString(PyExc_ValueError, "invalid authinfo type");
++		return NULL;
++	}
++
++	if (libiscsi_node_set_auth(context, &node->node, authinfo)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++	Py_RETURN_NONE;
++}
++
++static PyObject *PyIscsiNode_getAuth(PyObject *self)
++{
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	PyIscsiChapAuthInfo *pyauthinfo;
++	struct libiscsi_auth_info authinfo;
++
++	if (libiscsi_node_get_auth(context, &node->node, &authinfo)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++
++	switch (authinfo.method) {
++	case libiscsi_auth_chap:
++		pyauthinfo = PyObject_New(PyIscsiChapAuthInfo,
++					  &PyIscsiChapAuthInfo_Type);
++		if (!pyauthinfo)
++			return NULL;
++
++		pyauthinfo->info = authinfo;
++
++		return (PyObject *)pyauthinfo;
++
++	case libiscsi_auth_none:
++	default:
++		Py_RETURN_NONE;
++	}
++}
++
++static PyObject *PyIscsiNode_setParameter(PyObject *self, PyObject *args,
++					  PyObject *kwds)
++{
++	char *kwlist[] = {"parameter", "value", NULL};
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	const char *parameter, *value;
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwlist,
++					 &parameter, &value))
++		return NULL;
++	if (check_string(parameter) || check_string(value))
++		return NULL;
++
++	if (libiscsi_node_set_parameter(context, &node->node, parameter,
++				        value)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++	Py_RETURN_NONE;
++}
++
++static PyObject *PyIscsiNode_getParameter(PyObject *self, PyObject *args,
++					  PyObject *kwds)
++{
++	char *kwlist[] = {"parameter", NULL};
++	PyIscsiNode *node = (PyIscsiNode *)self;
++	const char *parameter;
++	char value[LIBISCSI_VALUE_MAXLEN];
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &parameter))
++		return NULL;
++	if (check_string(parameter))
++		return NULL;
++
++	if (libiscsi_node_get_parameter(context, &node->node, parameter,
++					value)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++	return Py_BuildValue("s", value);
++}
++
++static struct PyGetSetDef PyIscsiNode_getseters[] = {
++	{"name", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
++		"name", "name"},
++	{"tpgt", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
++		"tpgt", "tpgt"},
++	{"address", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
++		"address", "address"},
++	{"port", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
++		"port", "port"},
++	{"iface", (getter)PyIscsiNode_get, (setter)PyIscsiNode_set,
++		"iface", "iface"},
++	{NULL}
++};
++
++static struct PyMethodDef  PyIscsiNode_methods[] = {
++	{"login", (PyCFunction) PyIscsiNode_login, METH_NOARGS,
++		"Log in to the node"},
++	{"logout", (PyCFunction) PyIscsiNode_logout, METH_NOARGS,
++		"Log out of the node"},
++	{"setAuth", (PyCFunction) PyIscsiNode_setAuth,
++		METH_VARARGS|METH_KEYWORDS,
++		"Set authentication information"},
++	{"getAuth", (PyCFunction) PyIscsiNode_getAuth, METH_NOARGS,
++		"Get authentication information"},
++	{"setParameter", (PyCFunction) PyIscsiNode_setParameter,
++		METH_VARARGS|METH_KEYWORDS,
++		"Set an iscsi node parameter"},
++	{"getParameter", (PyCFunction) PyIscsiNode_getParameter,
++		METH_VARARGS|METH_KEYWORDS,
++		"Get an iscsi node parameter"},
++	{NULL}
++};
++
++PyTypeObject PyIscsiNode_Type = {
++	PyObject_HEAD_INIT(NULL)
++	.tp_name = "libiscsi.node",
++	.tp_basicsize = sizeof (PyIscsiNode),
++	.tp_getset = PyIscsiNode_getseters,
++	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
++		    Py_TPFLAGS_BASETYPE,
++	.tp_methods = PyIscsiNode_methods,
++	.tp_compare = (cmpfunc)PyIscsiNode_compare,
++	.tp_init = PyIscsiNode_init,
++	.tp_str = PyIscsiNode_str,
++	.tp_new = PyType_GenericNew,
++	.tp_doc = "The iscsi node contains iscsi node information.",
++};
++
++/***************************************************************************/
++
++static PyObject *pylibiscsi_discover_sendtargets(PyObject *self,
++						PyObject *args, PyObject *kwds)
++{
++	char *kwlist[] = {"address", "port", "authinfo", NULL};
++	const char *address = NULL;
++	int i, nr_found, port = 3260;
++	PyObject *authinfo_arg = NULL;
++	const struct libiscsi_auth_info *authinfo = NULL;
++	struct libiscsi_node *found_nodes;
++	PyObject* found_node_list;
++
++	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|iO",
++					kwlist, &address, &port,
++					&authinfo_arg))
++		return NULL;
++
++	if (authinfo_arg) {
++		if (PyObject_IsInstance(authinfo_arg, (PyObject *)
++					       &PyIscsiChapAuthInfo_Type)) {
++			PyIscsiChapAuthInfo *pyauthinfo =
++				(PyIscsiChapAuthInfo *)authinfo_arg;
++			authinfo = &pyauthinfo->info;
++		} else if (authinfo_arg != Py_None) {
++			PyErr_SetString(PyExc_ValueError,
++				"invalid authinfo type");
++			return NULL;
++		}
++	}
++
++	if (libiscsi_discover_sendtargets(context, address, port, authinfo,
++					  &nr_found, &found_nodes)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++
++	if (nr_found == 0)
++		Py_RETURN_NONE;
++
++	found_node_list = PyList_New(nr_found);
++	if (!found_node_list)
++		return NULL;
++
++	for(i = 0; i < nr_found; i++) {
++		PyIscsiNode *pynode;
++		
++		pynode = PyObject_New(PyIscsiNode, &PyIscsiNode_Type);
++		if (!pynode) {
++			/* This will deref already added nodes for us */
++			Py_DECREF(found_node_list);
++			return NULL;
++		}
++		pynode->node = found_nodes[i];
++		PyList_SET_ITEM(found_node_list, i, (PyObject *)pynode);
++	}
++
++	return found_node_list;	
++}
++
++static PyObject *pylibiscsi_discover_firmware(PyObject *self)
++{
++	int i, nr_found;
++	struct libiscsi_node *found_nodes;
++	PyObject* found_node_list;
++
++	if (libiscsi_discover_firmware(context, &nr_found, &found_nodes)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++
++	if (nr_found == 0)
++		Py_RETURN_NONE;
++
++	found_node_list = PyList_New(nr_found);
++	if (!found_node_list)
++		return NULL;
++
++	for(i = 0; i < nr_found; i++) {
++		PyIscsiNode *pynode;
++		
++		pynode = PyObject_New(PyIscsiNode, &PyIscsiNode_Type);
++		if (!pynode) {
++			/* This will deref already added nodes for us */
++			Py_DECREF(found_node_list);
++			return NULL;
++		}
++		pynode->node = found_nodes[i];
++		PyList_SET_ITEM(found_node_list, i, (PyObject *)pynode);
++	}
++
++	return found_node_list;	
++}
++
++static PyObject *pylibiscsi_get_firmware_initiator_name(PyObject *self)
++{
++	char initiatorname[LIBISCSI_VALUE_MAXLEN];
++
++	if (libiscsi_get_firmware_initiator_name(initiatorname)) {
++		PyErr_SetString(PyExc_IOError,
++				libiscsi_get_error_string(context));
++		return NULL;
++	}
++
++	return PyString_FromString(initiatorname);
++}
++
++static PyMethodDef pylibiscsi_functions[] = {
++	{	"discover_sendtargets",
++		(PyCFunction)pylibiscsi_discover_sendtargets,
++		METH_VARARGS|METH_KEYWORDS,
++		"Do sendtargets discovery and return a list of found nodes)"},
++	{	"discover_firmware",
++		(PyCFunction)pylibiscsi_discover_firmware, METH_NOARGS,
++		"Do firmware discovery and return a list of found nodes)"},
++	{	"get_firmware_initiator_name",
++		(PyCFunction)pylibiscsi_get_firmware_initiator_name,
++		METH_NOARGS,
++		"Get initator name (iqn) from firmware"},
++	{NULL, NULL}
++};
++
++PyMODINIT_FUNC initlibiscsi(void)
++{
++	PyObject *m;
++
++	if (!context) /* We may be called more then once */
++		context = libiscsi_init();
++	if (!context)
++		return;
++
++	if (PyType_Ready(&PyIscsiChapAuthInfo_Type) < 0)
++		return;
++
++	if (PyType_Ready(&PyIscsiNode_Type) < 0)
++		return;
++
++	m = Py_InitModule("libiscsi", pylibiscsi_functions);
++	Py_INCREF(&PyIscsiChapAuthInfo_Type);
++	PyModule_AddObject(m, "chapAuthInfo", (PyObject *) &PyIscsiChapAuthInfo_Type);
++	Py_INCREF(&PyIscsiNode_Type);
++	PyModule_AddObject(m, "node", (PyObject *) &PyIscsiNode_Type);
++}
+diff --git a/libiscsi/setup.py b/libiscsi/setup.py
+new file mode 100644
+index 000000000000..bb4329bc2158
+--- /dev/null
++++ b/libiscsi/setup.py
+@@ -0,0 +1,9 @@
++from distutils.core import setup, Extension
++
++module1 = Extension('libiscsimodule',
++                    sources = ['pylibiscsi.c'],
++                    libraries = ['iscsi'],
++                    library_dirs = ['.'])
++
++setup (name = 'PyIscsi',version = '1.0',
++       description = 'libiscsi python bindings', ext_modules = [module1])
+diff --git a/libiscsi/tests/test_discovery_firmware.c b/libiscsi/tests/test_discovery_firmware.c
+new file mode 100644
+index 000000000000..76e852a37019
+--- /dev/null
++++ b/libiscsi/tests/test_discovery_firmware.c
+@@ -0,0 +1,53 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include "libiscsi.h"
++
++int main(void)
++{
++	struct libiscsi_node *found_nodes;
++	struct libiscsi_context *context;
++	int i, found, rc = 0;
++
++	context = libiscsi_init();
++	if (!context) {
++		fprintf(stderr, "Error initializing libiscsi\n");
++		return 1;
++	}
++
++	rc = libiscsi_discover_firmware(context, &found, &found_nodes);
++	if (rc)
++		fprintf(stderr, "Error discovering: %s\n",
++			libiscsi_get_error_string(context));
++
++	for (i = 0; i < found; i++) {
++		fprintf(stdout, "Found node: %s, tpgt: %d, portal: %s:%d\n",
++			found_nodes[i].name, found_nodes[i].tpgt,
++			found_nodes[i].address,	found_nodes[i].port);
++	}
++
++	libiscsi_cleanup(context);
++	free (found_nodes);
++
++	return rc;
++}
+diff --git a/libiscsi/tests/test_discovery_sendtargets.c b/libiscsi/tests/test_discovery_sendtargets.c
+new file mode 100644
+index 000000000000..1a3c12ef684b
+--- /dev/null
++++ b/libiscsi/tests/test_discovery_sendtargets.c
+@@ -0,0 +1,60 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include "libiscsi.h"
++
++int main(void)
++{
++	struct libiscsi_node *found_nodes;
++	struct libiscsi_context *context;
++	struct libiscsi_auth_info auth_info;
++	int i, found, rc = 0;
++
++	context = libiscsi_init();
++	if (!context) {
++		fprintf(stderr, "Error initializing libiscsi\n");
++		return 1;
++	}
++
++	memset(&auth_info, 0, sizeof(auth_info));
++	auth_info.method = libiscsi_auth_chap;
++	strcpy(auth_info.chap.username, "joe");
++	strcpy(auth_info.chap.password, "secret");
++
++	rc = libiscsi_discover_sendtargets(context, "127.0.0.1", 3260,
++					   &auth_info, &found, &found_nodes);
++	if (rc)
++		fprintf(stderr, "Error discovering: %s\n",
++			libiscsi_get_error_string(context));
++
++	for (i = 0; i < found; i++) {
++		fprintf(stdout, "Found node: %s, tpgt: %d, portal: %s:%d\n",
++			found_nodes[i].name, found_nodes[i].tpgt,
++			found_nodes[i].address,	found_nodes[i].port);
++	}
++
++	libiscsi_cleanup(context);
++	free (found_nodes);
++
++	return rc;
++}
+diff --git a/libiscsi/tests/test_get_auth.c b/libiscsi/tests/test_get_auth.c
+new file mode 100644
+index 000000000000..5e234dadd0de
+--- /dev/null
++++ b/libiscsi/tests/test_get_auth.c
+@@ -0,0 +1,70 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include "libiscsi.h"
++
++int main(void)
++{
++	struct libiscsi_node node;
++	struct libiscsi_context *context;
++	struct libiscsi_auth_info auth_info;
++	int rc = 0;
++
++	snprintf(node.name, LIBISCSI_VALUE_MAXLEN, "%s",
++		 "iqn.2009-01.com.example:testdisk");
++	node.tpgt = 1;
++	snprintf(node.address, NI_MAXHOST, "%s", "127.0.0.1");
++	node.port = 3260;
++
++	context = libiscsi_init();
++	if (!context) {
++		fprintf(stderr, "Error initializing libiscsi\n");
++		return 1;
++	}
++
++	rc = libiscsi_node_get_auth(context, &node, &auth_info);
++	if (rc) {
++		fprintf(stderr, "Error setting authinfo: %s\n",
++			libiscsi_get_error_string(context));
++		goto leave;
++	}
++
++	switch (auth_info.method) {
++		case libiscsi_auth_none:
++			printf("Method:  \"None\"\n");
++			break;
++		case libiscsi_auth_chap:
++			printf("Method:  \"CHAP\"\n");
++			printf("User:    \"%s\"\n", auth_info.chap.username);
++			printf("Pass:    \"%s\"\n", auth_info.chap.password);
++			printf("RevUser: \"%s\"\n",
++				auth_info.chap.reverse_username);
++			printf("RevPass: \"%s\"\n",
++				auth_info.chap.reverse_password);
++			break;
++	}
++leave:
++	libiscsi_cleanup(context);
++
++	return rc;
++}
+diff --git a/libiscsi/tests/test_get_initiator_name.c b/libiscsi/tests/test_get_initiator_name.c
+new file mode 100644
+index 000000000000..997c053e5bf6
+--- /dev/null
++++ b/libiscsi/tests/test_get_initiator_name.c
+@@ -0,0 +1,38 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include "libiscsi.h"
++
++int main(void)
++{
++	char initiatorname[LIBISCSI_VALUE_MAXLEN];
++
++	if (libiscsi_get_firmware_initiator_name(initiatorname)) {
++		fprintf(stderr, "No iscsi boot firmware found\n");
++		return 1;
++	}
++
++	printf("iqn:\t%s\n", initiatorname);
++
++	return 0;
++}
+diff --git a/libiscsi/tests/test_get_network_config.c b/libiscsi/tests/test_get_network_config.c
+new file mode 100644
+index 000000000000..2dedd6102858
+--- /dev/null
++++ b/libiscsi/tests/test_get_network_config.c
+@@ -0,0 +1,45 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include "libiscsi.h"
++
++int main(void)
++{
++	struct libiscsi_network_config config;
++
++	if (libiscsi_get_firmware_network_config(&config)) {
++		fprintf(stderr, "No iscsi boot firmware found\n");
++		return 1;
++	}
++
++	printf("dhcp:\t%d\n", config.dhcp);
++	printf("iface:\t%s\n", config.iface_name);
++	printf("mac:\t%s\n", config.mac_address);
++	printf("ipaddr:\t%s\n", config.ip_address);
++	printf("mask:\t%s\n", config.netmask);
++	printf("gate:\t%s\n", config.gateway);
++	printf("dns1:\t%s\n", config.primary_dns);
++	printf("dns2:\t%s\n", config.secondary_dns);
++
++	return 0;
++}
+diff --git a/libiscsi/tests/test_login.c b/libiscsi/tests/test_login.c
+new file mode 100644
+index 000000000000..3eb70d63e111
+--- /dev/null
++++ b/libiscsi/tests/test_login.c
+@@ -0,0 +1,52 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include "libiscsi.h"
++
++int main(void)
++{
++	struct libiscsi_node node;
++	struct libiscsi_context *context;
++	int rc = 0;
++
++	snprintf(node.name, LIBISCSI_VALUE_MAXLEN, "%s",
++		 "iqn.2009-01.com.example:testdisk");
++	node.tpgt = 1;
++	snprintf(node.address, NI_MAXHOST, "%s", "127.0.0.1");
++	node.port = 3260;
++
++	context = libiscsi_init();
++	if (!context) {
++		fprintf(stderr, "Error initializing libiscsi\n");
++		return 1;
++	}
++
++	rc = libiscsi_node_login(context, &node);
++	if (rc)
++		fprintf(stderr, "Error logging in: %s\n",
++			libiscsi_get_error_string(context));
++
++	libiscsi_cleanup(context);
++
++	return rc;
++}
+diff --git a/libiscsi/tests/test_logout.c b/libiscsi/tests/test_logout.c
+new file mode 100644
+index 000000000000..b734dca58773
+--- /dev/null
++++ b/libiscsi/tests/test_logout.c
+@@ -0,0 +1,51 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include "libiscsi.h"
++
++int main(void)
++{
++	struct libiscsi_node node;
++	struct libiscsi_context *context;
++	int rc = 0;
++
++	snprintf(node.name, LIBISCSI_VALUE_MAXLEN, "%s",
++		 "iqn.2009-01.com.example:testdisk");
++	node.tpgt = 1;
++	snprintf(node.address, NI_MAXHOST, "%s", "127.0.0.1");
++	node.port = 3260;
++	
++	context = libiscsi_init();
++	if (!context) {
++		fprintf(stderr, "Error initializing libiscsi\n");
++		return 1;
++	}
++
++	rc = libiscsi_node_logout(context, &node);
++	if (rc)
++		fprintf(stderr, "Error logging out: %s\n",
++			libiscsi_get_error_string(context));
++
++	libiscsi_cleanup(context);
++
++	return rc;
++}
+diff --git a/libiscsi/tests/test_params.c b/libiscsi/tests/test_params.c
+new file mode 100644
+index 000000000000..d3223be1e894
+--- /dev/null
++++ b/libiscsi/tests/test_params.c
+@@ -0,0 +1,103 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <string.h>
++#include "libiscsi.h"
++
++int main(void)
++{
++	struct libiscsi_node node;
++	struct libiscsi_context *context;
++	char orig_value[LIBISCSI_VALUE_MAXLEN], value[LIBISCSI_VALUE_MAXLEN];
++	int rc = 0;
++
++	snprintf(node.name, LIBISCSI_VALUE_MAXLEN, "%s",
++		 "iqn.2009-01.com.example:testdisk");
++	node.tpgt = 1;
++	snprintf(node.address, NI_MAXHOST, "%s", "127.0.0.1");
++	node.port = 3260;
++
++	context = libiscsi_init();
++	if (!context) {
++		fprintf(stderr, "Error initializing libiscsi\n");
++		return 1;
++	}
++
++	rc = libiscsi_node_get_parameter(context, &node, "node.startup",
++		orig_value);
++	if (rc) {
++		fprintf(stderr, "Error getting original value: %s\n",
++			libiscsi_get_error_string(context));
++		goto leave;
++	}
++
++	rc = libiscsi_node_set_parameter(context, &node, "node.startup",
++		"automatic");
++	if (rc) {
++		fprintf(stderr, "Error setting node startup param: %s\n",
++			libiscsi_get_error_string(context));
++		goto leave;
++	}
++
++	rc = libiscsi_node_get_parameter(context, &node, "node.startup",
++		value);
++	if (rc) {
++		fprintf(stderr, "Error getting node startup param: %s\n",
++			libiscsi_get_error_string(context));
++		goto leave;
++	}
++	
++	if (strcmp(value, "automatic")) {
++		fprintf(stderr, "Error set and get values do not match!\n");
++		rc = EIO;
++		goto leave;
++	}
++
++	rc = libiscsi_node_set_parameter(context, &node, "node.startup",
++		orig_value);
++	if (rc) {
++		fprintf(stderr, "Error setting original value: %s\n",
++			libiscsi_get_error_string(context));
++		goto leave;
++	}
++
++	rc = libiscsi_node_get_parameter(context, &node, "node.startup",
++		value);
++	if (rc) {
++		fprintf(stderr, "Error re-getting original value: %s\n",
++			libiscsi_get_error_string(context));
++		goto leave;
++	}
++
++	if (strcmp(value, orig_value)) {
++		fprintf(stderr,
++			"Error set and get original values do not match!\n");
++		rc = EIO;
++		goto leave;
++	}
++
++leave:
++	libiscsi_cleanup(context);
++
++	return rc;
++}
+diff --git a/libiscsi/tests/test_set_auth.c b/libiscsi/tests/test_set_auth.c
+new file mode 100644
+index 000000000000..a21f88806ab9
+--- /dev/null
++++ b/libiscsi/tests/test_set_auth.c
+@@ -0,0 +1,58 @@
++/*
++ * iSCSI Administration library
++ *
++ * Copyright (C) 2008-2009 Red Hat, Inc. All rights reserved.
++ * Copyright (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
++ * maintained by open-iscsi@googlegroups.com
++ *
++ * 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 2 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.
++ *
++ * See the file COPYING included with this distribution for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include "libiscsi.h"
++
++int main(void)
++{
++	struct libiscsi_node node;
++	struct libiscsi_context *context;
++	struct libiscsi_auth_info auth_info;
++	int rc = 0;
++
++	snprintf(node.name, LIBISCSI_VALUE_MAXLEN, "%s",
++		 "iqn.2009-01.com.example:testdisk");
++	node.tpgt = 1;
++	snprintf(node.address, NI_MAXHOST, "%s", "127.0.0.1");
++	node.port = 3260;
++
++	memset(&auth_info, 0, sizeof(auth_info));
++	auth_info.method = libiscsi_auth_chap;
++	strcpy(auth_info.chap.username, "joe");
++	strcpy(auth_info.chap.password, "secret");
++
++	context = libiscsi_init();
++	if (!context) {
++		fprintf(stderr, "Error initializing libiscsi\n");
++		return 1;
++	}
++
++	rc = libiscsi_node_set_auth(context, &node, &auth_info);
++	if (rc)
++		fprintf(stderr, "Error setting authinfo: %s\n",
++			libiscsi_get_error_string(context));
++
++	libiscsi_cleanup(context);
++
++	return rc;
++}
+diff --git a/usr/Makefile b/usr/Makefile
+index c1866b6a4ed8..6d3ce2e8b6ce 100644
+--- a/usr/Makefile
++++ b/usr/Makefile
+@@ -30,7 +30,7 @@ endif
+ 
+ CFLAGS ?= -O2 -g
+ WARNFLAGS ?= -Wall -Wstrict-prototypes
+-CFLAGS += $(WARNFLAGS) -I../include -I. -D$(OSNAME) $(IPC_CFLAGS)
++CFLAGS += $(WARNFLAGS) -I../include -I. -D$(OSNAME) $(IPC_CFLAGS) -DISNS_ENABLE
+ PROGRAMS = iscsid iscsiadm iscsistart
+ 
+ # libc compat files
+diff --git a/usr/discovery.c b/usr/discovery.c
+index 593d22650f0d..de8267f08f8a 100644
+--- a/usr/discovery.c
++++ b/usr/discovery.c
+@@ -36,6 +36,7 @@
+ #include "types.h"
+ #include "iscsi_proto.h"
+ #include "initiator.h"
++#include "config.h"
+ #include "log.h"
+ #include "idbm.h"
+ #include "iscsi_settings.h"
+@@ -49,10 +50,12 @@
+ #include "iface.h"
+ #include "iscsi_timer.h"
+ #include "iscsi_err.h"
++#ifdef ISNS_ENABLE
+ /* libisns includes */
+ #include <libisns/isns.h>
+ #include <libisns/paths.h>
+ #include <libisns/message.h>
++#endif
+ 
+ #ifdef SLP_ENABLE
+ #include "iscsi-slp-discovery.h"
+@@ -98,6 +101,7 @@ static int request_initiator_name(void)
+ 	return 0;
+ }
+ 
++#ifdef ISNS_ENABLE
+ void discovery_isns_free_servername(void)
+ {
+ 	if (isns_config.ic_server_name)
+@@ -377,6 +381,7 @@ retry:
+ 	discovery_isns_free_servername();
+ 	return rc;
+ }
++#endif
+ 
+ int discovery_fw(void *data, struct iface_rec *iface,
+ 		 struct list_head *rec_list)
+diff --git a/usr/idbm.c b/usr/idbm.c
+index a749cc8ea3f8..979fefb7a5d7 100644
+--- a/usr/idbm.c
++++ b/usr/idbm.c
+@@ -1709,9 +1709,9 @@ int idbm_print_all_discovery(int info_level)
+  * fn should return -1 if it skipped the rec, an ISCSI_ERR error code if
+  * the operation failed or 0 if fn was run successfully.
+  */
+-static int idbm_for_each_iface(int *found, void *data,
+-				idbm_iface_op_fn *fn,
+-				char *targetname, int tpgt, char *ip, int port)
++int idbm_for_each_iface(int *found, void *data,
++			idbm_iface_op_fn *fn,
++			char *targetname, int tpgt, char *ip, int port)
+ {
+ 	DIR *iface_dirfd;
+ 	struct dirent *iface_dent;
+diff --git a/usr/idbm.h b/usr/idbm.h
+index b89ddffe55df..36312621f06c 100644
+--- a/usr/idbm.h
++++ b/usr/idbm.h
+@@ -102,6 +102,9 @@ struct rec_op_data {
+ 	node_rec_t *match_rec;
+ 	idbm_iface_op_fn *fn;
+ };
++extern int idbm_for_each_iface(int *found, void *data,
++			       idbm_iface_op_fn *fn,
++			       char *targetname, int tpgt, char *ip, int port);
+ extern int idbm_for_each_portal(int *found, void *data,
+ 				idbm_portal_op_fn *fn, char *targetname);
+ extern int idbm_for_each_node(int *found, void *data,
+diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h
+index 5087b5c6343d..a6dc40a55d78 100644
+--- a/usr/iscsi_ipc.h
++++ b/usr/iscsi_ipc.h
+@@ -166,4 +166,6 @@ struct iscsi_ipc {
+ 			 char *host_stats);
+ };
+ 
++struct iscsi_ipc *ipc;
++
+ #endif /* ISCSI_IPC_H */
+-- 
+2.9.3
+
diff --git a/SOURCES/0156-remove-the-offload-boot-supported-ifdef.patch b/SOURCES/0156-remove-the-offload-boot-supported-ifdef.patch
new file mode 100644
index 0000000..a1ccdc7
--- /dev/null
+++ b/SOURCES/0156-remove-the-offload-boot-supported-ifdef.patch
@@ -0,0 +1,45 @@
+From aa58a042ec20575143c1a5c813c9552a286aeb0e Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Mon, 19 Nov 2012 17:09:24 -0800
+Subject: remove the offload boot supported ifdef
+
+---
+ usr/iface.c | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+diff --git a/usr/iface.c b/usr/iface.c
+index c86892e..f5441c0 100644
+--- a/usr/iface.c
++++ b/usr/iface.c
+@@ -895,6 +895,7 @@ int iface_setup_from_boot_context(struct iface_rec *iface,
+ {
+ 	struct iscsi_transport *t = NULL;
+ 	uint32_t hostno;
++	int rc;
+ 
+ 	if (strlen(context->initiatorname))
+ 		strlcpy(iface->iname, context->initiatorname,
+@@ -907,10 +908,7 @@ int iface_setup_from_boot_context(struct iface_rec *iface,
+ 			return 0;
+ 		}
+ 	} else if (strlen(context->iface)) {
+-/* this ifdef is only temp until distros and firmwares are updated */
+-#ifdef OFFLOAD_BOOT_SUPPORTED
+ 		char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN];
+-		int rc;
+ 
+ 		memset(transport_name, 0, ISCSI_TRANSPORT_NAME_MAXLEN);
+ 		/* make sure offload driver is loaded */
+@@ -936,9 +934,6 @@ int iface_setup_from_boot_context(struct iface_rec *iface,
+ 		}
+ 
+ 		strlcpy(iface->netdev, context->iface, sizeof(iface->netdev));
+-#else
+-		return 0;
+-#endif
+ 	} else
+ 		return 0;
+ 
+-- 
+1.7.11.7
+
diff --git a/SOURCES/0159-iscsiuio-systemd-unit-files.patch b/SOURCES/0159-iscsiuio-systemd-unit-files.patch
new file mode 100644
index 0000000..2d0e213
--- /dev/null
+++ b/SOURCES/0159-iscsiuio-systemd-unit-files.patch
@@ -0,0 +1,53 @@
+From 822b53e6c9ebb0fe7236ebd3b4c73b009100592d Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Tue, 22 Jan 2013 14:27:12 -0800
+Subject: iscsiuio systemd unit files
+
+---
+ etc/systemd/iscsiuio.service | 17 +++++++++++++++++
+ etc/systemd/iscsiuio.socket  |  9 +++++++++
+ 2 files changed, 26 insertions(+)
+ create mode 100644 etc/systemd/iscsiuio.service
+ create mode 100644 etc/systemd/iscsiuio.socket
+
+diff --git a/etc/systemd/iscsiuio.service b/etc/systemd/iscsiuio.service
+new file mode 100644
+index 0000000..f0410b7
+--- /dev/null
++++ b/etc/systemd/iscsiuio.service
+@@ -0,0 +1,17 @@
++[Unit]
++Description=iSCSI UserSpace I/O driver
++Documentation=man:iscsiuio(8)
++DefaultDependencies=no
++Conflicts=shutdown.target
++Requires=iscsid.service
++BindTo=iscsid.service
++After=network.target
++Before=remote-fs-pre.target iscsid.service
++
++[Service]
++Type=forking
++PIDFile=/var/run/iscsiuio.pid
++ExecStart=/usr/sbin/iscsiuio
++
++[Install]
++WantedBy=multi-user.target
+diff --git a/etc/systemd/iscsiuio.socket b/etc/systemd/iscsiuio.socket
+new file mode 100644
+index 0000000..d42cedc
+--- /dev/null
++++ b/etc/systemd/iscsiuio.socket
+@@ -0,0 +1,9 @@
++[Unit]
++Description=Open-iSCSI iscsiuio Socket
++Documentation=man:iscsiuio(8)
++
++[Socket]
++ListenStream=@ISCSID_UIP_ABSTRACT_NAMESPACE
++
++[Install]
++WantedBy=sockets.target
+-- 
+1.7.11.7
+
diff --git a/SOURCES/0160-use-systemctl-to-start-iscsid.patch b/SOURCES/0160-use-systemctl-to-start-iscsid.patch
new file mode 100644
index 0000000..81aa2a3
--- /dev/null
+++ b/SOURCES/0160-use-systemctl-to-start-iscsid.patch
@@ -0,0 +1,25 @@
+From c3d2b8f3de5b6161845304cf46982d2c5a9918b6 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Thu Feb 21 21:05:39 PST 2013
+Subject: disable iscsid.startup from iscsiadm, prefer systemd socket activation
+
+---
+ etc/iscsid.conf | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/etc/iscsid.conf b/etc/iscsid.conf
+index ac1d231..5851fa5 100644
+--- a/etc/iscsid.conf
++++ b/etc/iscsid.conf
+@@ -17,7 +17,7 @@
+ # maintainers.
+ #
+ # Default for Fedora and RHEL. (uncomment to activate).
+-iscsid.startup = /etc/rc.d/init.d/iscsid force-start
++#iscsid.startup = /bin/systemctl start iscsid.service
+ # 
+ # Default for upstream open-iscsi scripts (uncomment to activate).
+ # iscsid.startup = /sbin/iscsid
+-- 
+1.7.11.7
+
diff --git a/SOURCES/0161-resolve-565245-multilib-issues-caused-by-doxygen.patch b/SOURCES/0161-resolve-565245-multilib-issues-caused-by-doxygen.patch
new file mode 100644
index 0000000..1f64d9c
--- /dev/null
+++ b/SOURCES/0161-resolve-565245-multilib-issues-caused-by-doxygen.patch
@@ -0,0 +1,39 @@
+From bc4cf1487b4d6039de2a082c1786ac83ab148c88 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Tue, 22 Jan 2013 15:14:21 -0800
+Subject: resolve 565245: multilib issues caused by doxygen
+
+---
+ libiscsi/libiscsi.doxy       | 2 +-
+ libiscsi/no_date_footer.html | 6 ++++++
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+ create mode 100644 libiscsi/no_date_footer.html
+
+diff --git a/libiscsi/libiscsi.doxy b/libiscsi/libiscsi.doxy
+index 663770f..7a5ff7f 100644
+--- a/libiscsi/libiscsi.doxy
++++ b/libiscsi/libiscsi.doxy
+@@ -765,7 +765,7 @@ HTML_HEADER            =
+ # each generated HTML page. If it is left blank doxygen will generate a 
+ # standard footer.
+ 
+-HTML_FOOTER            = 
++HTML_FOOTER            = no_date_footer.html
+ 
+ # The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+ # style sheet that is used by each HTML page. It can be used to 
+diff --git a/libiscsi/no_date_footer.html b/libiscsi/no_date_footer.html
+new file mode 100644
+index 0000000..1e0c6c4
+--- /dev/null
++++ b/libiscsi/no_date_footer.html
+@@ -0,0 +1,6 @@
++<hr size="1"><address style="text-align: right;"><small>
++Generated for $projectname by <a href="http://www.doxygen.org/
++index.html"><img src="doxygen.png" alt="doxygen" align="middle" border="0"></a>
++$doxygenversion</small></address>
++</body>
++</html>
+-- 
+1.7.11.7
+
diff --git a/SOURCES/0162-Don-t-check-for-autostart-sessions-if-iscsi-is-not-u.patch b/SOURCES/0162-Don-t-check-for-autostart-sessions-if-iscsi-is-not-u.patch
new file mode 100644
index 0000000..684463f
--- /dev/null
+++ b/SOURCES/0162-Don-t-check-for-autostart-sessions-if-iscsi-is-not-u.patch
@@ -0,0 +1,30 @@
+From ab79bdb20e37216ca969e06d63a952acfd023963 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Tue, 28 May 2013 13:12:27 -0700
+Subject: [PATCH] Don't check for autostart sessions if iscsi is not used (bug
+ #951951)
+
+Change conditional startup in iscsi.service to check for a non-empty
+nodes directory, instead of initiator-name.  This fits better with what
+it's doing, as there's no need to scan for autostart node records if
+there are no node records at all.
+---
+ etc/systemd/iscsi.service | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/etc/systemd/iscsi.service b/etc/systemd/iscsi.service
+index bbd52fd..7b4efee 100644
+--- a/etc/systemd/iscsi.service
++++ b/etc/systemd/iscsi.service
+@@ -5,7 +5,7 @@ DefaultDependencies=no
+ Conflicts=shutdown.target
+ After=systemd-remount-fs.service network.target iscsid.service iscsiuio.service
+ Before=remote-fs-pre.target
+-ConditionPathExists=/etc/iscsi/initiatorname.iscsi
++ConditionDirectoryNotEmpty=/var/lib/iscsi/nodes
+ 
+ [Service]
+ Type=oneshot
+-- 
+1.8.1.4
+
diff --git a/SOURCES/0164-libiscsi-fix-incorrect-strncpy-use.patch b/SOURCES/0164-libiscsi-fix-incorrect-strncpy-use.patch
new file mode 100644
index 0000000..55d1166
--- /dev/null
+++ b/SOURCES/0164-libiscsi-fix-incorrect-strncpy-use.patch
@@ -0,0 +1,52 @@
+From fcad7de1a8c3d140d1d0eb120727966017d3727b Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Sat, 17 Aug 2013 15:50:45 -0700
+Subject: libiscsi: fix incorrect strncpy use
+
+Changes to internal structures make the src and dst buffers of some
+copies (potentially) different sizes.  Fix strncpy calls that were using
+the size of the src argument as the limit.
+---
+ libiscsi/libiscsi.c | 19 ++++++++-----------
+ 1 file changed, 8 insertions(+), 11 deletions(-)
+
+diff --git a/libiscsi/libiscsi.c b/libiscsi/libiscsi.c
+index 6e6846a..064e4b5 100644
+--- a/libiscsi/libiscsi.c
++++ b/libiscsi/libiscsi.c
+@@ -587,15 +587,13 @@ int libiscsi_get_firmware_network_config(
+ 		return ENODEV;
+ 
+ 	config->dhcp = strlen(fw_entry.dhcp) ? 1 : 0;
+-	strncpy(config->iface_name, fw_entry.iface, sizeof fw_entry.iface);
+-	strncpy(config->mac_address, fw_entry.mac, sizeof fw_entry.mac);
+-	strncpy(config->ip_address, fw_entry.ipaddr, sizeof fw_entry.ipaddr);
+-	strncpy(config->netmask, fw_entry.mask, sizeof fw_entry.mask);
+-	strncpy(config->gateway, fw_entry.gateway, sizeof fw_entry.gateway);
+-	strncpy(config->primary_dns, fw_entry.primary_dns,
+-		sizeof fw_entry.primary_dns);
+-	strncpy(config->secondary_dns, fw_entry.secondary_dns,
+-		sizeof fw_entry.secondary_dns);
++	strlcpy(config->iface_name, fw_entry.iface, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->mac_address, fw_entry.mac, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->ip_address, fw_entry.ipaddr, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->netmask, fw_entry.mask, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->gateway, fw_entry.gateway, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->primary_dns, fw_entry.primary_dns, LIBISCSI_VALUE_MAXLEN);
++	strlcpy(config->secondary_dns, fw_entry.secondary_dns, LIBISCSI_VALUE_MAXLEN);
+ 	return 0;
+ }
+ 
+@@ -613,8 +611,7 @@ int libiscsi_get_firmware_initiator_name(char *initiatorname)
+ 	if (fw_get_entry(&fw_entry))
+ 		return ENODEV;
+ 
+-	strncpy(initiatorname, fw_entry.initiatorname,
+-		sizeof fw_entry.initiatorname);
++	strlcpy(initiatorname, fw_entry.initiatorname, LIBISCSI_VALUE_MAXLEN);
+ 
+ 	return 0;
+ }
+-- 
+1.8.1.4
+
diff --git a/SOURCES/0166-start-socket-listeners-on-iscsiadm-command.patch b/SOURCES/0166-start-socket-listeners-on-iscsiadm-command.patch
new file mode 100644
index 0000000..cb363b7
--- /dev/null
+++ b/SOURCES/0166-start-socket-listeners-on-iscsiadm-command.patch
@@ -0,0 +1,28 @@
+From 89e9c2ff66d069b812fabcd4fefe453bbcea73e4 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Mon, 25 Nov 2013 22:28:12 -0800
+Subject: [PATCH] start socket listeners on iscsiadm command
+
+fix for trying to run iscsiadm commands right after installing the rpm
+without manually starting the systemd units
+---
+ etc/iscsid.conf | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/etc/iscsid.conf b/etc/iscsid.conf
+index 1fd3000..412f130 100644
+--- a/etc/iscsid.conf
++++ b/etc/iscsid.conf
+@@ -17,7 +17,8 @@
+ # maintainers.
+ #
+ # Default for Fedora and RHEL. (uncomment to activate).
+-#iscsid.startup = /bin/systemctl start iscsid.service
++# Use socket activation, but try to make sure the socket units are listening
++iscsid.startup = /bin/systemctl start iscsid.socket iscsiuio.socket
+ # 
+ # Default for upstream open-iscsi scripts (uncomment to activate).
+ # iscsid.startup = /sbin/iscsid
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0167-Revert-iscsiadm-return-error-when-login-fails.patch b/SOURCES/0167-Revert-iscsiadm-return-error-when-login-fails.patch
new file mode 100644
index 0000000..64c9af8
--- /dev/null
+++ b/SOURCES/0167-Revert-iscsiadm-return-error-when-login-fails.patch
@@ -0,0 +1,35 @@
+From 1c0f37a8ae48daa3ae1c37cdac7c0789299180eb Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Mon, 24 Feb 2014 09:33:33 -0800
+Subject: [PATCH] Revert "iscsiadm: return error when login fails"
+
+This reverts commit fc2a8e9a2911bc76f961fe3e4a159fab9b8b9691.
+
+Done to address RHBZ #1015563
+
+---
+ usr/session_mgmt.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/usr/session_mgmt.c b/usr/session_mgmt.c
+index 87b8e00..3815b1d 100644
+--- a/usr/session_mgmt.c
++++ b/usr/session_mgmt.c
+@@ -178,12 +178,12 @@ int iscsi_login_portal(void *data, struct list_head *list, struct node_rec *rec)
+ 		goto done;
+ 	}
+ 	if (session_count >= rec->session.nr_sessions) {
+-		log_warning("%s: %d session%s requested, but %d "
++		log_debug(1, "%s: %d session%s requested, but %d "
+ 			  "already present.",
+ 			  rec->iface.name, rec->session.nr_sessions,
+ 			  rec->session.nr_sessions == 1 ? "" : "s",
+ 			  session_count);
+-		rc = ISCSI_ERR_SESS_EXISTS;
++		rc = 0;
+ 		goto done;
+ 	}
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0168-update-handling-of-boot-sessions.patch b/SOURCES/0168-update-handling-of-boot-sessions.patch
new file mode 100644
index 0000000..9c638c1
--- /dev/null
+++ b/SOURCES/0168-update-handling-of-boot-sessions.patch
@@ -0,0 +1,55 @@
+From 83f291bd475f3d11abaf1f7346732f75af585ed8 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Wed, 26 Feb 2014 16:33:48 -0800
+Subject: update handling of boot sessions
+
+force start iscsiuio if needed, socket activation does not seem to be
+working for recovery
+---
+ etc/systemd/iscsi-mark-root-nodes | 29 +++++++++++++++++++++++------
+ 1 file changed, 23 insertions(+), 6 deletions(-)
+
+diff --git a/etc/systemd/iscsi-mark-root-nodes b/etc/systemd/iscsi-mark-root-nodes
+index 157be62..d106ac6 100644
+--- a/etc/systemd/iscsi-mark-root-nodes
++++ b/etc/systemd/iscsi-mark-root-nodes
+@@ -1,13 +1,30 @@
+ #!/bin/bash
+ 
+ ISCSIADM=/sbin/iscsiadm
++start_iscsid=0
++start_iscsiuio=0
+ 
+-$ISCSIADM -m session >/dev/null 2>&1 || exit 0
++while read t num p target flash; do
++  # strip tag number from portal, keep "ip:port"
++  portal=${p%,*}
++  transport=${t%:}
+ 
+-$ISCSIADM -m session | while read t num i target; do
+-  ip=${i%:*}
+-  $ISCSIADM -m node -p $ip -T $target -o update -n node.startup -v onboot
+-done
++  $ISCSIADM -m node -p $portal -T $target -o update -n node.startup -v onboot
+ 
+-systemctl start iscsid.service
++  start_iscsid=1
++
++  if [ "$transport" = bnx2i ] || [ "$transport" = qedi ]; then
++    start_iscsiuio=1
++  fi
++done < <( $ISCSIADM -m session )
++
++# force iscsid and iscsiuio to start if needed for
++# recovering sessions created in the initrd
++
++if [ "$start_iscsid" -eq 1 ]; then
++  systemctl --no-block start iscsid.service
++fi
++if [ "$start_iscsiuio" -eq 1 ]; then
++  systemctl --no-block start iscsiuio.service
++fi
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0169-update-iscsi.service-for-boot-session-recovery.patch b/SOURCES/0169-update-iscsi.service-for-boot-session-recovery.patch
new file mode 100644
index 0000000..f7a2f79
--- /dev/null
+++ b/SOURCES/0169-update-iscsi.service-for-boot-session-recovery.patch
@@ -0,0 +1,34 @@
+From 969e26197c792ec5377d2c261a934a9c907e82f0 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Fri, 14 Mar 2014 09:22:21 -0700
+Subject: [PATCH] update iscsi.service for boot session recovery
+
+---
+ etc/systemd/iscsi.service | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/etc/systemd/iscsi.service b/etc/systemd/iscsi.service
+index d5712bd..3de76c5 100644
+--- a/etc/systemd/iscsi.service
++++ b/etc/systemd/iscsi.service
+@@ -5,14 +5,15 @@ DefaultDependencies=no
+ Conflicts=shutdown.target
+ After=systemd-remount-fs.service network.target iscsid.service iscsiuio.service
+ Before=remote-fs-pre.target
+-ConditionDirectoryNotEmpty=/var/lib/iscsi/nodes
++ConditionDirectoryNotEmpty=|/var/lib/iscsi/nodes
++ConditionDirectoryNotEmpty=|/sys/class/iscsi_session
+ 
+ [Service]
+ Type=oneshot
+ RemainAfterExit=true
+ ExecStart=/usr/libexec/iscsi-mark-root-nodes
++SuccessExitStatus=21
+ ExecStart=/sbin/iscsiadm -m node --loginall=automatic
+-ExecStop=/bin/sync
+ ExecStop=/sbin/iscsiadm -m node --logoutall=automatic
+ ExecReload=/sbin/iscsiadm -m node --loginall=automatic
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0170-fix-systemd-unit-wants.patch b/SOURCES/0170-fix-systemd-unit-wants.patch
new file mode 100644
index 0000000..45ce32a
--- /dev/null
+++ b/SOURCES/0170-fix-systemd-unit-wants.patch
@@ -0,0 +1,41 @@
+From 347e6120213efda47a45443b4e366ed1400433c1 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Wed, 17 Sep 2014 09:58:39 -0700
+Subject: [PATCH] updates to iscsi.service
+
+Resolves: #1126524
+Resolves: #1111925
+---
+ etc/systemd/iscsi.service | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/etc/systemd/iscsi.service b/etc/systemd/iscsi.service
+index 3de76c5..ad7be34 100644
+--- a/etc/systemd/iscsi.service
++++ b/etc/systemd/iscsi.service
+@@ -5,17 +5,17 @@ DefaultDependencies=no
+ Conflicts=shutdown.target
+ After=systemd-remount-fs.service network.target iscsid.service iscsiuio.service
+ Before=remote-fs-pre.target
++Wants=remote-fs-pre.target
+ ConditionDirectoryNotEmpty=|/var/lib/iscsi/nodes
+ ConditionDirectoryNotEmpty=|/sys/class/iscsi_session
+ 
+ [Service]
+ Type=oneshot
+ RemainAfterExit=true
+-ExecStart=/usr/libexec/iscsi-mark-root-nodes
+-SuccessExitStatus=21
+-ExecStart=/sbin/iscsiadm -m node --loginall=automatic
+-ExecStop=/sbin/iscsiadm -m node --logoutall=automatic
+-ExecReload=/sbin/iscsiadm -m node --loginall=automatic
++ExecStart=-/usr/libexec/iscsi-mark-root-nodes
++ExecStart=-/sbin/iscsiadm -m node --loginall=automatic
++ExecStop=-/sbin/iscsiadm -m node --logoutall=automatic
++ExecReload=-/sbin/iscsiadm -m node --loginall=automatic
+ 
+ [Install]
+ WantedBy=sysinit.target
+-- 
+1.9.3
+
diff --git a/SOURCES/0172-move-cleanup-to-seperate-service.patch b/SOURCES/0172-move-cleanup-to-seperate-service.patch
new file mode 100644
index 0000000..f54de39
--- /dev/null
+++ b/SOURCES/0172-move-cleanup-to-seperate-service.patch
@@ -0,0 +1,56 @@
+From 56d9d1c6a02dcad0915c0673f9cd2e653c86302f Mon Sep 17 00:00:00 2001
+From: rpm-build <rpm-build>
+Date: Tue, 13 Jan 2015 16:30:01 -0800
+Subject: [PATCH] 0171-make-session-shutdown-a-seperate-service.patch
+
+---
+ etc/systemd/iscsi-shutdown.service | 14 ++++++++++++++
+ etc/systemd/iscsi.service          |  3 +--
+ 2 files changed, 15 insertions(+), 2 deletions(-)
+ create mode 100644 etc/systemd/iscsi-shutdown.service
+
+diff --git a/etc/systemd/iscsi-shutdown.service b/etc/systemd/iscsi-shutdown.service
+new file mode 100644
+index 0000000..23758e9
+--- /dev/null
++++ b/etc/systemd/iscsi-shutdown.service
+@@ -0,0 +1,15 @@
++[Unit]
++Description=Logout off all iSCSI sessions on shutdown
++Documentation=man:iscsid(8) man:iscsiadm(8)
++DefaultDependencies=no
++Conflicts=shutdown.target
++After=systemd-remount-fs.service network.target iscsid.service iscsiuio.service
++Before=remote-fs-pre.target
++Wants=remote-fs-pre.target
++RefuseManualStop=yes
++
++[Service]
++Type=oneshot
++RemainAfterExit=true
++ExecStart=-/bin/true
++ExecStop=-/sbin/iscsiadm -m node --logoutall=all
+diff --git a/etc/systemd/iscsi.service b/etc/systemd/iscsi.service
+index ad7be34..2736956 100644
+--- a/etc/systemd/iscsi.service
++++ b/etc/systemd/iscsi.service
+@@ -5,7 +5,7 @@ DefaultDependencies=no
+ Conflicts=shutdown.target
+ After=systemd-remount-fs.service network.target iscsid.service iscsiuio.service
+ Before=remote-fs-pre.target
+-Wants=remote-fs-pre.target
++Wants=remote-fs-pre.target iscsi-shutdown.service
+ ConditionDirectoryNotEmpty=|/var/lib/iscsi/nodes
+ ConditionDirectoryNotEmpty=|/sys/class/iscsi_session
+ 
+@@ -14,7 +14,6 @@ Type=oneshot
+ RemainAfterExit=true
+ ExecStart=-/usr/libexec/iscsi-mark-root-nodes
+ ExecStart=-/sbin/iscsiadm -m node --loginall=automatic
+-ExecStop=-/sbin/iscsiadm -m node --logoutall=automatic
+ ExecReload=-/sbin/iscsiadm -m node --loginall=automatic
+ 
+ [Install]
+-- 
+2.1.0
+
diff --git a/SOURCES/0199-use-Red-Hat-version-string-to-match-RPM-package-vers.patch b/SOURCES/0199-use-Red-Hat-version-string-to-match-RPM-package-vers.patch
new file mode 100644
index 0000000..d45b83e
--- /dev/null
+++ b/SOURCES/0199-use-Red-Hat-version-string-to-match-RPM-package-vers.patch
@@ -0,0 +1,25 @@
+From 8f7669c48198989eac5e08664133f56371ad963b Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Mon, 21 Jan 2013 15:43:36 -0800
+Subject: use Red Hat version string to match RPM package version
+
+---
+ usr/version.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/usr/version.h b/usr/version.h
+index 20f07946be1f..baa8c00c7185 100644
+--- a/usr/version.h
++++ b/usr/version.h
+@@ -6,7 +6,7 @@
+  * This may not be the same value as the kernel versions because
+  * some other maintainer could merge a patch without going through us
+  */
+-#define ISCSI_VERSION_STR	"2.0-874"
++#define ISCSI_VERSION_STR	"6.2.0.874-7"
+ #define ISCSI_VERSION_FILE	"/sys/module/scsi_transport_iscsi/version"
+ 
+ #endif
+-- 
+2.9.3
+
diff --git a/SOURCES/04-iscsi b/SOURCES/04-iscsi
new file mode 100755
index 0000000..864ade7
--- /dev/null
+++ b/SOURCES/04-iscsi
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+case "$2" in
+	up|vpn-up)
+		/bin/systemctl --no-block reload iscsi.service || :
+		;;
+esac
diff --git a/SOURCES/be2iscsi-vlan.patch b/SOURCES/be2iscsi-vlan.patch
new file mode 100644
index 0000000..7a48644
--- /dev/null
+++ b/SOURCES/be2iscsi-vlan.patch
@@ -0,0 +1,191 @@
+commit ea9c14d9ab10d1070a8d3c032e64bb946a279a02
+Author: Chris Leech <cleech@redhat.com>
+Date:   Thu Nov 30 12:05:28 2017 -0800
+
+    vlan setting sync for be2iscsi
+
+diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
+index b30518a293db..f269fc406a13 100644
+--- a/usr/iscsiadm.c
++++ b/usr/iscsiadm.c
+@@ -2311,6 +2311,89 @@ static int verify_iface_params(struct list_head *params, struct node_rec *rec)
+ 	return 0;
+ }
+ 
++static int iface_param_update(struct iface_rec *iface, struct list_head *params)
++{
++	struct node_rec *rec;
++	int rc = 0;
++
++	rec = idbm_create_rec(NULL, -1, NULL, -1, iface, 1);
++	if (!rec) {
++		rc = ISCSI_ERR_INVAL;
++		goto update_fail;
++	}
++
++	if (iscsi_check_for_running_session(rec))
++		log_warning("Updating iface while iscsi sessions "
++			    "are using it. You must logout the running "
++			    "sessions then log back in for the "
++			    "new settings to take affect.");
++
++	rc = verify_iface_params(params, rec);
++	if (rc)
++		goto update_fail;
++
++	rc = iface_conf_update(params, &rec->iface);
++	if (rc)
++		goto update_fail;
++
++	rc = __for_each_matched_rec(0, rec, params, idbm_node_set_param);
++	if (rc == ISCSI_ERR_NO_OBJS_FOUND)
++		rc = 0;
++	else if (rc)
++		goto update_fail;
++
++	printf("%s updated.\n", iface->name);
++	free(rec);
++	return rc;
++
++update_fail:
++	log_error("Could not update iface %s: %s",
++		  iface->name, iscsi_err_to_str(rc));
++	free(rec);
++	return rc;
++}
++
++struct iface_param_sync {
++	struct iface_rec *primary;
++	struct list_head *params;
++	int count;
++};
++
++static int update_sync_params(void *data, struct iface_rec *iface)
++{
++	struct iface_param_sync *iface_params = data;
++	struct iface_rec *primary = iface_params->primary;
++	struct list_head *params = iface_params->params;
++
++	if ((strcmp(primary->transport_name, iface->transport_name)) ||
++	    (strcmp(primary->hwaddress, iface->hwaddress)) ||
++	    (primary->iface_num != iface->iface_num))
++		return 0;
++
++	return iface_param_update(iface, params);
++}
++
++static int split_vlan_params(struct list_head *params, struct list_head *vlan_params)
++{
++	struct user_param *param, *tmp;
++
++	list_for_each_entry_safe(param, tmp, params, list) {
++		if (!strncmp(param->name, "iface.vlan", 10)) {
++			list_move_tail(&param->list, vlan_params);
++		}
++	}
++	return 0;
++}
++
++static inline void list_splice_tail(struct list_head *list, struct list_head *head)
++{
++	list->prev->next = head;
++	list->next->prev = head->prev;
++	head->prev->next = list->next;
++	head->prev = list->prev;
++	INIT_LIST_HEAD(list);
++}
++
+ /* TODO: merge iter helpers and clean them up, so we can use them here */
+ static int exec_iface_op(int op, int do_show, int info_level,
+ 			 struct iface_rec *iface, uint64_t host_no,
+@@ -2319,6 +2402,8 @@ static int exec_iface_op(int op, int do_show, int info_level,
+ 	struct host_info hinfo;
+ 	struct node_rec *rec = NULL;
+ 	int rc = 0;
++	LIST_HEAD(vlan_params);
++	struct iscsi_transport *t;
+ 
+ 	switch (op) {
+ 	case OP_NEW:
+@@ -2381,36 +2466,27 @@ delete_fail:
+ 		rec = idbm_create_rec(NULL, -1, NULL, -1, iface, 1);
+ 		if (!rec) {
+ 			rc = ISCSI_ERR_INVAL;
+-			goto update_fail;
++			break;
+ 		}
+-
+-		if (iscsi_check_for_running_session(rec))
+-			log_warning("Updating iface while iscsi sessions "
+-				    "are using it. You must logout the running "
+-				    "sessions then log back in for the "
+-				    "new settings to take affect.");
+-
+-		rc = verify_iface_params(params, rec);
+-		if (rc)
++		t = iscsi_sysfs_get_transport_by_name(rec->iface.transport_name);
++		if (!t) {
++			log_error("Cound not locate transport for iface %s", iface->name);
++			rc = ISCSI_ERR_INVAL;
+ 			break;
+-
+-		/* pass rec's iface because it has the db values */
+-		rc = iface_conf_update(params, &rec->iface);
+-		if (rc)
+-			goto update_fail;
+-
+-		rc = __for_each_matched_rec(0, rec, params,
+-					    idbm_node_set_param);
+-		if (rc == ISCSI_ERR_NO_OBJS_FOUND)
+-			rc = 0;
+-		else if (rc)
+-			goto update_fail;
+-
+-		printf("%s updated.\n", iface->name);
+-		break;
+-update_fail:
+-		log_error("Could not update iface %s: %s",
+-			  iface->name, iscsi_err_to_str(rc));
++		}
++		if (t->template->sync_vlan_settings) {
++			/* sync shared vlan settings across ifaces */
++			int nr_found = 0;
++			struct iface_param_sync sync_params = {
++				.primary = &rec->iface,
++				.params = &vlan_params,
++				.count = 0,
++			};
++			split_vlan_params(params, &vlan_params);
++			iface_for_each_iface(&sync_params, 1, &nr_found, update_sync_params);
++		}
++		iface_param_update(&rec->iface, params);
++		list_splice_tail(&vlan_params, params);
+ 		break;
+ 	case OP_APPLY:
+ 		if (!iface) {
+diff --git a/usr/transport.c b/usr/transport.c
+index 3b7a00a2245e..35a8ccd4a400 100644
+--- a/usr/transport.c
++++ b/usr/transport.c
+@@ -91,6 +91,7 @@ struct iscsi_transport_template bnx2i = {
+ struct iscsi_transport_template be2iscsi = {
+ 	.name		= "be2iscsi",
+         .bind_ep_required = 1,
++	.sync_vlan_settings = 1,
+ 	.create_conn	= be2iscsi_create_conn,
+ 	.ep_connect	= ktransport_ep_connect,
+ 	.ep_poll	= ktransport_ep_poll,
+diff --git a/usr/transport.h b/usr/transport.h
+index b67776b47288..07027564e46b 100644
+--- a/usr/transport.h
++++ b/usr/transport.h
+@@ -40,6 +40,9 @@ struct iscsi_transport_template {
+ 	uint8_t use_boot_info;
+         uint8_t bind_ep_required;
+ 	uint8_t no_netdev;
++	/* be2iscsi has a single host vlan setting,
++	 * but uses 2 ifaces for ipv4 and ipv6 settings; keep them in sync */
++	uint8_t sync_vlan_settings;
+ 	int (*ep_connect) (struct iscsi_conn *conn, int non_blocking);
+ 	int (*ep_poll) (struct iscsi_conn *conn, int timeout_ms);
+ 	void (*ep_disconnect) (struct iscsi_conn *conn);
diff --git a/SOURCES/iscsi-tmpfiles.conf b/SOURCES/iscsi-tmpfiles.conf
new file mode 100644
index 0000000..eab4fb2
--- /dev/null
+++ b/SOURCES/iscsi-tmpfiles.conf
@@ -0,0 +1,2 @@
+d /run/lock/iscsi 0700 root root -
+f /run/lock/iscsi/lock 0600 root root -
diff --git a/SOURCES/keep-open-isns.patch b/SOURCES/keep-open-isns.patch
new file mode 100644
index 0000000..b8b700e
--- /dev/null
+++ b/SOURCES/keep-open-isns.patch
@@ -0,0 +1,45649 @@
+From 791c2f1d7ada4e1eb2215164db3a8343005e30c2 Mon Sep 17 00:00:00 2001
+From: rpm-build <rpm-build>
+Date: Mon, 13 Jun 2016 16:11:01 -0700
+Subject: [PATCH] keep-open-isns.patch
+
+---
+ Makefile                                           |   12 +-
+ README                                             |   14 -
+ usr/Makefile                                       |    7 +-
+ usr/discovery.c                                    |    6 +-
+ usr/discoveryd.c                                   |    8 +-
+ usr/iscsiadm.c                                     |    2 +-
+ utils/open-isns/COPYING                            |  504 ++
+ utils/open-isns/ChangeLog                          |   50 +
+ utils/open-isns/HACKING                            |   30 +
+ utils/open-isns/Makefile.in                        |   81 +
+ utils/open-isns/README                             |  173 +
+ utils/open-isns/TODO                               |  100 +
+ utils/open-isns/aclocal/config.guess               | 1499 +++++
+ utils/open-isns/aclocal/config.sub                 | 1570 +++++
+ utils/open-isns/aclocal/install-sh                 |  251 +
+ utils/open-isns/attrs.c                            | 1618 +++++
+ utils/open-isns/attrs.h                            |  262 +
+ utils/open-isns/authblock.c                        |   62 +
+ utils/open-isns/bitvector.c                        |  648 ++
+ utils/open-isns/buffer.c                           |  407 ++
+ utils/open-isns/buffer.h                           |  141 +
+ utils/open-isns/callback.c                         |  148 +
+ utils/open-isns/client.c                           |  205 +
+ utils/open-isns/compat/my_getopt.c                 |  271 +
+ utils/open-isns/compat/my_getopt.h                 |   69 +
+ utils/open-isns/config.c                           |  278 +
+ utils/open-isns/config.h.in                        |  103 +
+ utils/open-isns/configure                          | 6727 ++++++++++++++++++++
+ utils/open-isns/configure.ac                       |  118 +
+ utils/open-isns/db-file.c                          |  615 ++
+ utils/open-isns/db-policy.c                        |  187 +
+ utils/open-isns/db.c                               |  994 +++
+ utils/open-isns/db.h                               |  147 +
+ utils/open-isns/dd.c                               | 1306 ++++
+ utils/open-isns/deregister.c                       |  271 +
+ utils/open-isns/doc/isns_config.5                  |  387 ++
+ utils/open-isns/doc/isnsadm.8                      |  688 ++
+ utils/open-isns/doc/isnsd.8                        |   93 +
+ utils/open-isns/doc/isnsdd.8                       |   75 +
+ utils/open-isns/domain.c                           |  208 +
+ utils/open-isns/entity.c                           |  127 +
+ utils/open-isns/error.c                            |   65 +
+ utils/open-isns/esi.c                              |  576 ++
+ utils/open-isns/etc/isnsadm.conf                   |   73 +
+ utils/open-isns/etc/isnsd.conf                     |  129 +
+ utils/open-isns/etc/isnsdd.conf                    |   72 +
+ utils/open-isns/etc/openisns.init                  |   71 +
+ utils/open-isns/export.c                           |  547 ++
+ utils/open-isns/getnext.c                          |  257 +
+ utils/open-isns/internal.h                         |   16 +
+ utils/open-isns/isns-proto.h                       |  259 +
+ utils/open-isns/isns.h                             |  673 ++
+ utils/open-isns/isnsadm.c                          | 1151 ++++
+ utils/open-isns/isnsd.c                            |  299 +
+ utils/open-isns/isnsdd.c                           | 1153 ++++
+ utils/open-isns/isnssetup                          |   52 +
+ utils/open-isns/local.c                            |  353 +
+ utils/open-isns/logging.c                          |  228 +
+ utils/open-isns/mdebug.c                           |  295 +
+ utils/open-isns/message.c                          |  681 ++
+ utils/open-isns/message.h                          |  196 +
+ utils/open-isns/objects.c                          | 1320 ++++
+ utils/open-isns/objects.h                          |  168 +
+ utils/open-isns/parser.c                           |  134 +
+ utils/open-isns/paths.h                            |   22 +
+ utils/open-isns/pidfile.c                          |   98 +
+ utils/open-isns/pki.c                              |  536 ++
+ utils/open-isns/policy.c                           |  577 ++
+ utils/open-isns/portal-group.c                     |  307 +
+ utils/open-isns/query.c                            |  238 +
+ utils/open-isns/register.c                         |  934 +++
+ utils/open-isns/relation.c                         |  281 +
+ utils/open-isns/scn.c                              |  926 +++
+ utils/open-isns/scope.c                            |  513 ++
+ utils/open-isns/security.c                         |  437 ++
+ utils/open-isns/security.h                         |  185 +
+ utils/open-isns/server.c                           |  236 +
+ utils/open-isns/simple.c                           |  727 +++
+ utils/open-isns/slp.c                              |  242 +
+ utils/open-isns/socket.c                           | 2304 +++++++
+ utils/open-isns/socket.h                           |   95 +
+ utils/open-isns/source.h                           |   32 +
+ utils/open-isns/storage-node.c                     |  202 +
+ utils/open-isns/sysdep-unix.c                      |  186 +
+ utils/open-isns/tags.c                             |  740 +++
+ utils/open-isns/tests/.cvsignore                   |    2 +
+ utils/open-isns/tests/Makefile                     |   40 +
+ utils/open-isns/tests/client.conf                  |    8 +
+ utils/open-isns/tests/data/test01/01-enroll        |   18 +
+ utils/open-isns/tests/data/test01/02-registration  |   42 +
+ utils/open-isns/tests/data/test01/03-query         |   20 +
+ utils/open-isns/tests/data/test01/03-registration  |   20 +
+ .../open-isns/tests/data/test01/99-unregistration  |   18 +
+ utils/open-isns/tests/data/test02/01-enroll        |   18 +
+ utils/open-isns/tests/data/test02/02-enroll        |   24 +
+ utils/open-isns/tests/data/test02/03-registration  |   72 +
+ utils/open-isns/tests/data/test02/04-query         |   20 +
+ utils/open-isns/tests/data/test02/05-query         |   20 +
+ .../open-isns/tests/data/test02/06-dd-registration |   81 +
+ utils/open-isns/tests/data/test02/07-query         |   40 +
+ utils/open-isns/tests/data/test02/08-query         |   40 +
+ utils/open-isns/tests/data/test02/09-query         |   20 +
+ .../open-isns/tests/data/test02/10-dd-registration |   87 +
+ utils/open-isns/tests/data/test02/11-query         |   10 +
+ .../tests/data/test02/12-dd-deregistration         |   85 +
+ .../tests/data/test02/13-dd-deregistration         |   83 +
+ .../open-isns/tests/data/test02/14-dd-registration |   85 +
+ .../tests/data/test02/15-dd-deregistration         |   76 +
+ utils/open-isns/tests/data/test03/01-enroll        |   18 +
+ utils/open-isns/tests/data/test03/02-registration  |   42 +
+ .../open-isns/tests/data/test03/03-unregistration  |   42 +
+ .../open-isns/tests/data/test03/04-unregistration  |   18 +
+ .../open-isns/tests/data/test03/99-unregistration  |   13 +
+ utils/open-isns/tests/data/test04/01-enroll        |   18 +
+ utils/open-isns/tests/data/test04/02-registration  |   42 +
+ utils/open-isns/tests/data/test04/03-restart       |   42 +
+ utils/open-isns/tests/data/test04/04-query         |   20 +
+ utils/open-isns/tests/data/test05/01-enroll        |   18 +
+ utils/open-isns/tests/data/test05/02-registration  |   42 +
+ utils/open-isns/tests/data/test05/03-expired       |   18 +
+ utils/open-isns/tests/data/test06/01-enroll        |   18 +
+ utils/open-isns/tests/data/test06/02-registration  |   42 +
+ utils/open-isns/tests/data/test06/03-registration  |   42 +
+ utils/open-isns/tests/data/test06/04-registration  |   42 +
+ .../open-isns/tests/data/test06/05-dd-registration |   49 +
+ utils/open-isns/tests/data/test06/06-registration  |   49 +
+ .../open-isns/tests/data/test06/07-dd-registration |   52 +
+ utils/open-isns/tests/data/test06/08-registration  |   64 +
+ utils/open-isns/tests/data/test06/09-registration  |   64 +
+ .../open-isns/tests/data/test06/10-unregistration  |   37 +
+ utils/open-isns/tests/data/test06/11-registration  |   52 +
+ utils/open-isns/tests/data/test07/01-enroll        |   19 +
+ utils/open-isns/tests/data/test07/02-registration  |   45 +
+ utils/open-isns/tests/data/test07/03-expired       |   19 +
+ utils/open-isns/tests/data/test07/04-registration  |   57 +
+ utils/open-isns/tests/data/test07/05-expired       |   19 +
+ utils/open-isns/tests/data/test08/01-pauw1         |  100 +
+ utils/open-isns/tests/data/test09/01-pauw2         |   31 +
+ utils/open-isns/tests/data/test10/01-pauw3         |   31 +
+ utils/open-isns/tests/data/test10/02-expired       |   31 +
+ utils/open-isns/tests/data/test10/03-pauw3         |   31 +
+ utils/open-isns/tests/data/test10/04-expired       |   31 +
+ utils/open-isns/tests/data/test11/01-pauw4         |   32 +
+ utils/open-isns/tests/genkey                       |  175 +
+ utils/open-isns/tests/harness.pl                   |  929 +++
+ utils/open-isns/tests/pauw1.c                      |  179 +
+ utils/open-isns/tests/pauw2.c                      |  212 +
+ utils/open-isns/tests/pauw3.c                      |  139 +
+ utils/open-isns/tests/pauw4.c                      |  137 +
+ utils/open-isns/tests/server.conf                  |   11 +
+ utils/open-isns/tests/test01.pl                    |   30 +
+ utils/open-isns/tests/test02.pl                    |   58 +
+ utils/open-isns/tests/test03.pl                    |   27 +
+ utils/open-isns/tests/test04.pl                    |   30 +
+ utils/open-isns/tests/test05.pl                    |   25 +
+ utils/open-isns/tests/test06.pl                    |   50 +
+ utils/open-isns/tests/test07.pl                    |   37 +
+ utils/open-isns/tests/test08.pl                    |   23 +
+ utils/open-isns/tests/test09.pl                    |   23 +
+ utils/open-isns/tests/test10.pl                    |   33 +
+ utils/open-isns/tests/test11.pl                    |   23 +
+ utils/open-isns/timer.c                            |  126 +
+ utils/open-isns/types.h                            |   57 +
+ utils/open-isns/util.c                             |  263 +
+ utils/open-isns/util.h                             |  289 +
+ utils/open-isns/vendor.c                           |   41 +
+ utils/open-isns/vendor.h                           |   56 +
+ 167 files changed, 44212 insertions(+), 28 deletions(-)
+ create mode 100644 utils/open-isns/COPYING
+ create mode 100644 utils/open-isns/ChangeLog
+ create mode 100644 utils/open-isns/HACKING
+ create mode 100644 utils/open-isns/Makefile.in
+ create mode 100644 utils/open-isns/README
+ create mode 100644 utils/open-isns/TODO
+ create mode 100644 utils/open-isns/aclocal/config.guess
+ create mode 100644 utils/open-isns/aclocal/config.sub
+ create mode 100644 utils/open-isns/aclocal/install-sh
+ create mode 100644 utils/open-isns/attrs.c
+ create mode 100644 utils/open-isns/attrs.h
+ create mode 100644 utils/open-isns/authblock.c
+ create mode 100644 utils/open-isns/bitvector.c
+ create mode 100644 utils/open-isns/buffer.c
+ create mode 100644 utils/open-isns/buffer.h
+ create mode 100644 utils/open-isns/callback.c
+ create mode 100644 utils/open-isns/client.c
+ create mode 100644 utils/open-isns/compat/my_getopt.c
+ create mode 100644 utils/open-isns/compat/my_getopt.h
+ create mode 100644 utils/open-isns/config.c
+ create mode 100644 utils/open-isns/config.h.in
+ create mode 100755 utils/open-isns/configure
+ create mode 100644 utils/open-isns/configure.ac
+ create mode 100644 utils/open-isns/db-file.c
+ create mode 100644 utils/open-isns/db-policy.c
+ create mode 100644 utils/open-isns/db.c
+ create mode 100644 utils/open-isns/db.h
+ create mode 100644 utils/open-isns/dd.c
+ create mode 100644 utils/open-isns/deregister.c
+ create mode 100644 utils/open-isns/doc/isns_config.5
+ create mode 100644 utils/open-isns/doc/isnsadm.8
+ create mode 100644 utils/open-isns/doc/isnsd.8
+ create mode 100644 utils/open-isns/doc/isnsdd.8
+ create mode 100644 utils/open-isns/domain.c
+ create mode 100644 utils/open-isns/entity.c
+ create mode 100644 utils/open-isns/error.c
+ create mode 100644 utils/open-isns/esi.c
+ create mode 100644 utils/open-isns/etc/isnsadm.conf
+ create mode 100644 utils/open-isns/etc/isnsd.conf
+ create mode 100644 utils/open-isns/etc/isnsdd.conf
+ create mode 100644 utils/open-isns/etc/openisns.init
+ create mode 100644 utils/open-isns/export.c
+ create mode 100644 utils/open-isns/getnext.c
+ create mode 100644 utils/open-isns/internal.h
+ create mode 100644 utils/open-isns/isns-proto.h
+ create mode 100644 utils/open-isns/isns.h
+ create mode 100644 utils/open-isns/isnsadm.c
+ create mode 100644 utils/open-isns/isnsd.c
+ create mode 100644 utils/open-isns/isnsdd.c
+ create mode 100644 utils/open-isns/isnssetup
+ create mode 100644 utils/open-isns/local.c
+ create mode 100644 utils/open-isns/logging.c
+ create mode 100644 utils/open-isns/mdebug.c
+ create mode 100644 utils/open-isns/message.c
+ create mode 100644 utils/open-isns/message.h
+ create mode 100644 utils/open-isns/objects.c
+ create mode 100644 utils/open-isns/objects.h
+ create mode 100644 utils/open-isns/parser.c
+ create mode 100644 utils/open-isns/paths.h
+ create mode 100644 utils/open-isns/pidfile.c
+ create mode 100644 utils/open-isns/pki.c
+ create mode 100644 utils/open-isns/policy.c
+ create mode 100644 utils/open-isns/portal-group.c
+ create mode 100644 utils/open-isns/query.c
+ create mode 100644 utils/open-isns/register.c
+ create mode 100644 utils/open-isns/relation.c
+ create mode 100644 utils/open-isns/scn.c
+ create mode 100644 utils/open-isns/scope.c
+ create mode 100644 utils/open-isns/security.c
+ create mode 100644 utils/open-isns/security.h
+ create mode 100644 utils/open-isns/server.c
+ create mode 100644 utils/open-isns/simple.c
+ create mode 100644 utils/open-isns/slp.c
+ create mode 100644 utils/open-isns/socket.c
+ create mode 100644 utils/open-isns/socket.h
+ create mode 100644 utils/open-isns/source.h
+ create mode 100644 utils/open-isns/storage-node.c
+ create mode 100644 utils/open-isns/sysdep-unix.c
+ create mode 100644 utils/open-isns/tags.c
+ create mode 100644 utils/open-isns/tests/.cvsignore
+ create mode 100644 utils/open-isns/tests/Makefile
+ create mode 100644 utils/open-isns/tests/client.conf
+ create mode 100644 utils/open-isns/tests/data/test01/01-enroll
+ create mode 100644 utils/open-isns/tests/data/test01/02-registration
+ create mode 100644 utils/open-isns/tests/data/test01/03-query
+ create mode 100644 utils/open-isns/tests/data/test01/03-registration
+ create mode 100644 utils/open-isns/tests/data/test01/99-unregistration
+ create mode 100644 utils/open-isns/tests/data/test02/01-enroll
+ create mode 100644 utils/open-isns/tests/data/test02/02-enroll
+ create mode 100644 utils/open-isns/tests/data/test02/03-registration
+ create mode 100644 utils/open-isns/tests/data/test02/04-query
+ create mode 100644 utils/open-isns/tests/data/test02/05-query
+ create mode 100644 utils/open-isns/tests/data/test02/06-dd-registration
+ create mode 100644 utils/open-isns/tests/data/test02/07-query
+ create mode 100644 utils/open-isns/tests/data/test02/08-query
+ create mode 100644 utils/open-isns/tests/data/test02/09-query
+ create mode 100644 utils/open-isns/tests/data/test02/10-dd-registration
+ create mode 100644 utils/open-isns/tests/data/test02/11-query
+ create mode 100644 utils/open-isns/tests/data/test02/12-dd-deregistration
+ create mode 100644 utils/open-isns/tests/data/test02/13-dd-deregistration
+ create mode 100644 utils/open-isns/tests/data/test02/14-dd-registration
+ create mode 100644 utils/open-isns/tests/data/test02/15-dd-deregistration
+ create mode 100644 utils/open-isns/tests/data/test03/01-enroll
+ create mode 100644 utils/open-isns/tests/data/test03/02-registration
+ create mode 100644 utils/open-isns/tests/data/test03/03-unregistration
+ create mode 100644 utils/open-isns/tests/data/test03/04-unregistration
+ create mode 100644 utils/open-isns/tests/data/test03/99-unregistration
+ create mode 100644 utils/open-isns/tests/data/test04/01-enroll
+ create mode 100644 utils/open-isns/tests/data/test04/02-registration
+ create mode 100644 utils/open-isns/tests/data/test04/03-restart
+ create mode 100644 utils/open-isns/tests/data/test04/04-query
+ create mode 100644 utils/open-isns/tests/data/test05/01-enroll
+ create mode 100644 utils/open-isns/tests/data/test05/02-registration
+ create mode 100644 utils/open-isns/tests/data/test05/03-expired
+ create mode 100644 utils/open-isns/tests/data/test06/01-enroll
+ create mode 100644 utils/open-isns/tests/data/test06/02-registration
+ create mode 100644 utils/open-isns/tests/data/test06/03-registration
+ create mode 100644 utils/open-isns/tests/data/test06/04-registration
+ create mode 100644 utils/open-isns/tests/data/test06/05-dd-registration
+ create mode 100644 utils/open-isns/tests/data/test06/06-registration
+ create mode 100644 utils/open-isns/tests/data/test06/07-dd-registration
+ create mode 100644 utils/open-isns/tests/data/test06/08-registration
+ create mode 100644 utils/open-isns/tests/data/test06/09-registration
+ create mode 100644 utils/open-isns/tests/data/test06/10-unregistration
+ create mode 100644 utils/open-isns/tests/data/test06/11-registration
+ create mode 100644 utils/open-isns/tests/data/test07/01-enroll
+ create mode 100644 utils/open-isns/tests/data/test07/02-registration
+ create mode 100644 utils/open-isns/tests/data/test07/03-expired
+ create mode 100644 utils/open-isns/tests/data/test07/04-registration
+ create mode 100644 utils/open-isns/tests/data/test07/05-expired
+ create mode 100644 utils/open-isns/tests/data/test08/01-pauw1
+ create mode 100644 utils/open-isns/tests/data/test09/01-pauw2
+ create mode 100644 utils/open-isns/tests/data/test10/01-pauw3
+ create mode 100644 utils/open-isns/tests/data/test10/02-expired
+ create mode 100644 utils/open-isns/tests/data/test10/03-pauw3
+ create mode 100644 utils/open-isns/tests/data/test10/04-expired
+ create mode 100644 utils/open-isns/tests/data/test11/01-pauw4
+ create mode 100644 utils/open-isns/tests/genkey
+ create mode 100644 utils/open-isns/tests/harness.pl
+ create mode 100644 utils/open-isns/tests/pauw1.c
+ create mode 100644 utils/open-isns/tests/pauw2.c
+ create mode 100644 utils/open-isns/tests/pauw3.c
+ create mode 100644 utils/open-isns/tests/pauw4.c
+ create mode 100644 utils/open-isns/tests/server.conf
+ create mode 100644 utils/open-isns/tests/test01.pl
+ create mode 100644 utils/open-isns/tests/test02.pl
+ create mode 100644 utils/open-isns/tests/test03.pl
+ create mode 100644 utils/open-isns/tests/test04.pl
+ create mode 100644 utils/open-isns/tests/test05.pl
+ create mode 100644 utils/open-isns/tests/test06.pl
+ create mode 100644 utils/open-isns/tests/test07.pl
+ create mode 100644 utils/open-isns/tests/test08.pl
+ create mode 100644 utils/open-isns/tests/test09.pl
+ create mode 100644 utils/open-isns/tests/test10.pl
+ create mode 100644 utils/open-isns/tests/test11.pl
+ create mode 100644 utils/open-isns/timer.c
+ create mode 100644 utils/open-isns/types.h
+ create mode 100644 utils/open-isns/util.c
+ create mode 100644 utils/open-isns/util.h
+ create mode 100644 utils/open-isns/vendor.c
+ create mode 100644 utils/open-isns/vendor.h
+
+diff --git a/Makefile b/Makefile
+index cf028cf..03175a3 100644
+--- a/Makefile
++++ b/Makefile
+@@ -7,7 +7,7 @@
+ DESTDIR ?= 
+ 
+ prefix = /usr
+-exec_prefix = /
++exec_prefix = /usr
+ sbindir = $(exec_prefix)/sbin
+ bindir = $(exec_prefix)/bin
+ mandir = $(prefix)/share/man
+@@ -25,7 +25,7 @@ ifdef OPTFLAGS
+ CFLAGS = $(OPTFLAGS)
+ endif
+ 
+-# Export it so configure of iscsiuio will
++# Export it so configure of iscsiuio & open-isns will
+ # pick it up.
+ ifneq (,$(CFLAGS))
+ export CFLAGS
+@@ -37,7 +37,8 @@ endif
+ 
+ all: user
+ 
+-user: iscsiuio/Makefile
++user: utils/open-isns/Makefile iscsiuio/Makefile
++	$(MAKE) -C utils/open-isns
+ 	$(MAKE) -C utils/sysdeps
+ 	$(MAKE) -C utils/fwparam_ibft
+ 	$(MAKE) -C usr
+@@ -54,6 +55,9 @@ user: iscsiuio/Makefile
+ 	@echo
+ 	@echo "Read README file for detailed information."
+ 
++utils/open-isns/Makefile: utils/open-isns/configure utils/open-isns/Makefile.in
++	cd utils/open-isns; ./configure --with-security=no
++
+ iscsiuio/Makefile: iscsiuio/configure iscsiuio/Makefile.in
+ 	cd iscsiuio; ./configure
+ 
+@@ -80,6 +84,8 @@ clean:
+ 	$(MAKE) -C kernel clean
+ 	[ ! -f iscsiuio/Makefile ] || $(MAKE) -C iscsiuio clean
+ 	[ ! -f iscsiuio/Makefile ] || $(MAKE) -C iscsiuio distclean
++	[ ! -f utils/open-isns/Makefile ] || $(MAKE) -C utils/open-isns clean
++	[ ! -f utils/open-isns/Makefile ] || $(MAKE) -C utils/open-isns distclean
+ 
+ # this is for safety
+ # now -jXXX will still be safe
+diff --git a/README b/README
+index cfc7f2f..c4fc3cf 100644
+--- a/README
++++ b/README
+@@ -74,20 +74,6 @@ the cache sync command will fail.
+ - iscsiadm's -P 3 option will not print out scsi devices.
+ - iscsid will not automatically online devices.
+ 
+-The userspace components: iscsid, iscsiadm and iscsistart require the
+-open-isns library which can be found here:
+-
+-https://github.com/gonzoleeman/open-isns/releases
+-
+-To install the open-isns headers and library required for open-iscsi download
+-the current release and run:
+-
+-	./configure
+-	make
+-	make install
+-	make install_hdrs
+-	make install_lib
+-
+ By default the kernel's iSCSI modules will be used. Running:
+ 
+ 	make
+diff --git a/usr/Makefile b/usr/Makefile
+index 6d3ce2e..865d001 100644
+--- a/usr/Makefile
++++ b/usr/Makefile
+@@ -30,7 +30,8 @@ endif
+ 
+ CFLAGS ?= -O2 -g
+ WARNFLAGS ?= -Wall -Wstrict-prototypes
+-CFLAGS += $(WARNFLAGS) -I../include -I. -D$(OSNAME) $(IPC_CFLAGS) -DISNS_ENABLE
++CFLAGS += $(WARNFLAGS) -I../include -I. -I../utils/open-isns \
++				-D$(OSNAME) $(IPC_CFLAGS) -DISNS_ENABLE
+ PROGRAMS = iscsid iscsiadm iscsistart
+ 
+ # libc compat files
+@@ -54,10 +55,10 @@ all: $(PROGRAMS)
+ 
+ iscsid: $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(DISCOVERY_SRCS) \
+ 	iscsid.o session_mgmt.o discoveryd.o mntcheck.o
+-	$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@  -lisns -lcrypto -lrt -lmount
++	$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -L../utils/open-isns -lisns -lrt -lmount
+ 
+ iscsiadm: $(ISCSI_LIB_SRCS) $(DISCOVERY_SRCS) iscsiadm.o session_mgmt.o mntcheck.o
+-	$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -lisns -lcrypto -lmount
++	$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ -L../utils/open-isns -lisns -lmount
+ 
+ iscsistart: $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(FW_BOOT_SRCS) \
+ 		iscsistart.o statics.o
+diff --git a/usr/discovery.c b/usr/discovery.c
+index de8267f..de8e81e 100644
+--- a/usr/discovery.c
++++ b/usr/discovery.c
+@@ -52,9 +52,9 @@
+ #include "iscsi_err.h"
+ #ifdef ISNS_ENABLE
+ /* libisns includes */
+-#include <libisns/isns.h>
+-#include <libisns/paths.h>
+-#include <libisns/message.h>
++#include "isns.h"
++#include "paths.h"
++#include "message.h"
+ #endif
+ 
+ #ifdef SLP_ENABLE
+diff --git a/usr/discoveryd.c b/usr/discoveryd.c
+index f5359c7..1e14977 100644
+--- a/usr/discoveryd.c
++++ b/usr/discoveryd.c
+@@ -40,11 +40,11 @@
+ #include "iface.h"
+ #include "session_mgmt.h"
+ #include "session_info.h"
++#include "isns-proto.h"
++#include "isns.h"
++#include "paths.h"
++#include "message.h"
+ #include "iscsi_err.h"
+-#include <libisns/isns-proto.h>
+-#include <libisns/isns.h>
+-#include <libisns/paths.h>
+-#include <libisns/message.h>
+ 
+ #define DISC_DEF_POLL_INVL	30
+ 
+diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
+index fc0c8bc..b916f51 100644
+--- a/usr/iscsiadm.c
++++ b/usr/iscsiadm.c
+@@ -49,7 +49,7 @@
+ #include "idbm_fields.h"
+ #include "session_mgmt.h"
+ #include "iscsid_req.h"
+-#include <libisns/isns-proto.h>
++#include "isns-proto.h"
+ #include "iscsi_err.h"
+ #include "iscsi_ipc.h"
+ #include "iscsi_timer.h"
+diff --git a/utils/open-isns/COPYING b/utils/open-isns/COPYING
+new file mode 100644
+index 0000000..b1e3f5a
+--- /dev/null
++++ b/utils/open-isns/COPYING
+@@ -0,0 +1,504 @@
++		  GNU LESSER GENERAL PUBLIC LICENSE
++		       Version 2.1, February 1999
++
++ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
++     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ Everyone is permitted to copy and distribute verbatim copies
++ of this license document, but changing it is not allowed.
++
++[This is the first released version of the Lesser GPL.  It also counts
++ as the successor of the GNU Library Public License, version 2, hence
++ the version number 2.1.]
++
++			    Preamble
++
++  The licenses for most software are designed to take away your
++freedom to share and change it.  By contrast, the GNU General Public
++Licenses are intended to guarantee your freedom to share and change
++free software--to make sure the software is free for all its users.
++
++  This license, the Lesser General Public License, applies to some
++specially designated software packages--typically libraries--of the
++Free Software Foundation and other authors who decide to use it.  You
++can use it too, but we suggest you first think carefully about whether
++this license or the ordinary General Public License is the better
++strategy to use in any particular case, based on the explanations below.
++
++  When we speak of free software, we are referring to freedom of use,
++not price.  Our General Public Licenses are designed to make sure that
++you have the freedom to distribute copies of free software (and charge
++for this service if you wish); that you receive source code or can get
++it if you want it; that you can change the software and use pieces of
++it in new free programs; and that you are informed that you can do
++these things.
++
++  To protect your rights, we need to make restrictions that forbid
++distributors to deny you these rights or to ask you to surrender these
++rights.  These restrictions translate to certain responsibilities for
++you if you distribute copies of the library or if you modify it.
++
++  For example, if you distribute copies of the library, whether gratis
++or for a fee, you must give the recipients all the rights that we gave
++you.  You must make sure that they, too, receive or can get the source
++code.  If you link other code with the library, you must provide
++complete object files to the recipients, so that they can relink them
++with the library after making changes to the library and recompiling
++it.  And you must show them these terms so they know their rights.
++
++  We protect your rights with a two-step method: (1) we copyright the
++library, and (2) we offer you this license, which gives you legal
++permission to copy, distribute and/or modify the library.
++
++  To protect each distributor, we want to make it very clear that
++there is no warranty for the free library.  Also, if the library is
++modified by someone else and passed on, the recipients should know
++that what they have is not the original version, so that the original
++author's reputation will not be affected by problems that might be
++introduced by others.
++
++  Finally, software patents pose a constant threat to the existence of
++any free program.  We wish to make sure that a company cannot
++effectively restrict the users of a free program by obtaining a
++restrictive license from a patent holder.  Therefore, we insist that
++any patent license obtained for a version of the library must be
++consistent with the full freedom of use specified in this license.
++
++  Most GNU software, including some libraries, is covered by the
++ordinary GNU General Public License.  This license, the GNU Lesser
++General Public License, applies to certain designated libraries, and
++is quite different from the ordinary General Public License.  We use
++this license for certain libraries in order to permit linking those
++libraries into non-free programs.
++
++  When a program is linked with a library, whether statically or using
++a shared library, the combination of the two is legally speaking a
++combined work, a derivative of the original library.  The ordinary
++General Public License therefore permits such linking only if the
++entire combination fits its criteria of freedom.  The Lesser General
++Public License permits more lax criteria for linking other code with
++the library.
++
++  We call this license the "Lesser" General Public License because it
++does Less to protect the user's freedom than the ordinary General
++Public License.  It also provides other free software developers Less
++of an advantage over competing non-free programs.  These disadvantages
++are the reason we use the ordinary General Public License for many
++libraries.  However, the Lesser license provides advantages in certain
++special circumstances.
++
++  For example, on rare occasions, there may be a special need to
++encourage the widest possible use of a certain library, so that it becomes
++a de-facto standard.  To achieve this, non-free programs must be
++allowed to use the library.  A more frequent case is that a free
++library does the same job as widely used non-free libraries.  In this
++case, there is little to gain by limiting the free library to free
++software only, so we use the Lesser General Public License.
++
++  In other cases, permission to use a particular library in non-free
++programs enables a greater number of people to use a large body of
++free software.  For example, permission to use the GNU C Library in
++non-free programs enables many more people to use the whole GNU
++operating system, as well as its variant, the GNU/Linux operating
++system.
++
++  Although the Lesser General Public License is Less protective of the
++users' freedom, it does ensure that the user of a program that is
++linked with the Library has the freedom and the wherewithal to run
++that program using a modified version of the Library.
++
++  The precise terms and conditions for copying, distribution and
++modification follow.  Pay close attention to the difference between a
++"work based on the library" and a "work that uses the library".  The
++former contains code derived from the library, whereas the latter must
++be combined with the library in order to run.
++
++		  GNU LESSER GENERAL PUBLIC LICENSE
++   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
++
++  0. This License Agreement applies to any software library or other
++program which contains a notice placed by the copyright holder or
++other authorized party saying it may be distributed under the terms of
++this Lesser General Public License (also called "this License").
++Each licensee is addressed as "you".
++
++  A "library" means a collection of software functions and/or data
++prepared so as to be conveniently linked with application programs
++(which use some of those functions and data) to form executables.
++
++  The "Library", below, refers to any such software library or work
++which has been distributed under these terms.  A "work based on the
++Library" means either the Library or any derivative work under
++copyright law: that is to say, a work containing the Library or a
++portion of it, either verbatim or with modifications and/or translated
++straightforwardly into another language.  (Hereinafter, translation is
++included without limitation in the term "modification".)
++
++  "Source code" for a work means the preferred form of the work for
++making modifications to it.  For a library, complete source code means
++all the source code for all modules it contains, plus any associated
++interface definition files, plus the scripts used to control compilation
++and installation of the library.
++
++  Activities other than copying, distribution and modification are not
++covered by this License; they are outside its scope.  The act of
++running a program using the Library is not restricted, and output from
++such a program is covered only if its contents constitute a work based
++on the Library (independent of the use of the Library in a tool for
++writing it).  Whether that is true depends on what the Library does
++and what the program that uses the Library does.
++  
++  1. You may copy and distribute verbatim copies of the Library's
++complete source code as you receive it, in any medium, provided that
++you conspicuously and appropriately publish on each copy an
++appropriate copyright notice and disclaimer of warranty; keep intact
++all the notices that refer to this License and to the absence of any
++warranty; and distribute a copy of this License along with the
++Library.
++
++  You may charge a fee for the physical act of transferring a copy,
++and you may at your option offer warranty protection in exchange for a
++fee.
++
++  2. You may modify your copy or copies of the Library or any portion
++of it, thus forming a work based on the Library, and copy and
++distribute such modifications or work under the terms of Section 1
++above, provided that you also meet all of these conditions:
++
++    a) The modified work must itself be a software library.
++
++    b) You must cause the files modified to carry prominent notices
++    stating that you changed the files and the date of any change.
++
++    c) You must cause the whole of the work to be licensed at no
++    charge to all third parties under the terms of this License.
++
++    d) If a facility in the modified Library refers to a function or a
++    table of data to be supplied by an application program that uses
++    the facility, other than as an argument passed when the facility
++    is invoked, then you must make a good faith effort to ensure that,
++    in the event an application does not supply such function or
++    table, the facility still operates, and performs whatever part of
++    its purpose remains meaningful.
++
++    (For example, a function in a library to compute square roots has
++    a purpose that is entirely well-defined independent of the
++    application.  Therefore, Subsection 2d requires that any
++    application-supplied function or table used by this function must
++    be optional: if the application does not supply it, the square
++    root function must still compute square roots.)
++
++These requirements apply to the modified work as a whole.  If
++identifiable sections of that work are not derived from the Library,
++and can be reasonably considered independent and separate works in
++themselves, then this License, and its terms, do not apply to those
++sections when you distribute them as separate works.  But when you
++distribute the same sections as part of a whole which is a work based
++on the Library, the distribution of the whole must be on the terms of
++this License, whose permissions for other licensees extend to the
++entire whole, and thus to each and every part regardless of who wrote
++it.
++
++Thus, it is not the intent of this section to claim rights or contest
++your rights to work written entirely by you; rather, the intent is to
++exercise the right to control the distribution of derivative or
++collective works based on the Library.
++
++In addition, mere aggregation of another work not based on the Library
++with the Library (or with a work based on the Library) on a volume of
++a storage or distribution medium does not bring the other work under
++the scope of this License.
++
++  3. You may opt to apply the terms of the ordinary GNU General Public
++License instead of this License to a given copy of the Library.  To do
++this, you must alter all the notices that refer to this License, so
++that they refer to the ordinary GNU General Public License, version 2,
++instead of to this License.  (If a newer version than version 2 of the
++ordinary GNU General Public License has appeared, then you can specify
++that version instead if you wish.)  Do not make any other change in
++these notices.
++
++  Once this change is made in a given copy, it is irreversible for
++that copy, so the ordinary GNU General Public License applies to all
++subsequent copies and derivative works made from that copy.
++
++  This option is useful when you wish to copy part of the code of
++the Library into a program that is not a library.
++
++  4. You may copy and distribute the Library (or a portion or
++derivative of it, under Section 2) in object code or executable form
++under the terms of Sections 1 and 2 above provided that you accompany
++it with the complete corresponding machine-readable source code, which
++must be distributed under the terms of Sections 1 and 2 above on a
++medium customarily used for software interchange.
++
++  If distribution of object code is made by offering access to copy
++from a designated place, then offering equivalent access to copy the
++source code from the same place satisfies the requirement to
++distribute the source code, even though third parties are not
++compelled to copy the source along with the object code.
++
++  5. A program that contains no derivative of any portion of the
++Library, but is designed to work with the Library by being compiled or
++linked with it, is called a "work that uses the Library".  Such a
++work, in isolation, is not a derivative work of the Library, and
++therefore falls outside the scope of this License.
++
++  However, linking a "work that uses the Library" with the Library
++creates an executable that is a derivative of the Library (because it
++contains portions of the Library), rather than a "work that uses the
++library".  The executable is therefore covered by this License.
++Section 6 states terms for distribution of such executables.
++
++  When a "work that uses the Library" uses material from a header file
++that is part of the Library, the object code for the work may be a
++derivative work of the Library even though the source code is not.
++Whether this is true is especially significant if the work can be
++linked without the Library, or if the work is itself a library.  The
++threshold for this to be true is not precisely defined by law.
++
++  If such an object file uses only numerical parameters, data
++structure layouts and accessors, and small macros and small inline
++functions (ten lines or less in length), then the use of the object
++file is unrestricted, regardless of whether it is legally a derivative
++work.  (Executables containing this object code plus portions of the
++Library will still fall under Section 6.)
++
++  Otherwise, if the work is a derivative of the Library, you may
++distribute the object code for the work under the terms of Section 6.
++Any executables containing that work also fall under Section 6,
++whether or not they are linked directly with the Library itself.
++
++  6. As an exception to the Sections above, you may also combine or
++link a "work that uses the Library" with the Library to produce a
++work containing portions of the Library, and distribute that work
++under terms of your choice, provided that the terms permit
++modification of the work for the customer's own use and reverse
++engineering for debugging such modifications.
++
++  You must give prominent notice with each copy of the work that the
++Library is used in it and that the Library and its use are covered by
++this License.  You must supply a copy of this License.  If the work
++during execution displays copyright notices, you must include the
++copyright notice for the Library among them, as well as a reference
++directing the user to the copy of this License.  Also, you must do one
++of these things:
++
++    a) Accompany the work with the complete corresponding
++    machine-readable source code for the Library including whatever
++    changes were used in the work (which must be distributed under
++    Sections 1 and 2 above); and, if the work is an executable linked
++    with the Library, with the complete machine-readable "work that
++    uses the Library", as object code and/or source code, so that the
++    user can modify the Library and then relink to produce a modified
++    executable containing the modified Library.  (It is understood
++    that the user who changes the contents of definitions files in the
++    Library will not necessarily be able to recompile the application
++    to use the modified definitions.)
++
++    b) Use a suitable shared library mechanism for linking with the
++    Library.  A suitable mechanism is one that (1) uses at run time a
++    copy of the library already present on the user's computer system,
++    rather than copying library functions into the executable, and (2)
++    will operate properly with a modified version of the library, if
++    the user installs one, as long as the modified version is
++    interface-compatible with the version that the work was made with.
++
++    c) Accompany the work with a written offer, valid for at
++    least three years, to give the same user the materials
++    specified in Subsection 6a, above, for a charge no more
++    than the cost of performing this distribution.
++
++    d) If distribution of the work is made by offering access to copy
++    from a designated place, offer equivalent access to copy the above
++    specified materials from the same place.
++
++    e) Verify that the user has already received a copy of these
++    materials or that you have already sent this user a copy.
++
++  For an executable, the required form of the "work that uses the
++Library" must include any data and utility programs needed for
++reproducing the executable from it.  However, as a special exception,
++the materials to be distributed need not include anything that is
++normally distributed (in either source or binary form) with the major
++components (compiler, kernel, and so on) of the operating system on
++which the executable runs, unless that component itself accompanies
++the executable.
++
++  It may happen that this requirement contradicts the license
++restrictions of other proprietary libraries that do not normally
++accompany the operating system.  Such a contradiction means you cannot
++use both them and the Library together in an executable that you
++distribute.
++
++  7. You may place library facilities that are a work based on the
++Library side-by-side in a single library together with other library
++facilities not covered by this License, and distribute such a combined
++library, provided that the separate distribution of the work based on
++the Library and of the other library facilities is otherwise
++permitted, and provided that you do these two things:
++
++    a) Accompany the combined library with a copy of the same work
++    based on the Library, uncombined with any other library
++    facilities.  This must be distributed under the terms of the
++    Sections above.
++
++    b) Give prominent notice with the combined library of the fact
++    that part of it is a work based on the Library, and explaining
++    where to find the accompanying uncombined form of the same work.
++
++  8. You may not copy, modify, sublicense, link with, or distribute
++the Library except as expressly provided under this License.  Any
++attempt otherwise to copy, modify, sublicense, link with, or
++distribute the Library is void, and will automatically terminate your
++rights under this License.  However, parties who have received copies,
++or rights, from you under this License will not have their licenses
++terminated so long as such parties remain in full compliance.
++
++  9. You are not required to accept this License, since you have not
++signed it.  However, nothing else grants you permission to modify or
++distribute the Library or its derivative works.  These actions are
++prohibited by law if you do not accept this License.  Therefore, by
++modifying or distributing the Library (or any work based on the
++Library), you indicate your acceptance of this License to do so, and
++all its terms and conditions for copying, distributing or modifying
++the Library or works based on it.
++
++  10. Each time you redistribute the Library (or any work based on the
++Library), the recipient automatically receives a license from the
++original licensor to copy, distribute, link with or modify the Library
++subject to these terms and conditions.  You may not impose any further
++restrictions on the recipients' exercise of the rights granted herein.
++You are not responsible for enforcing compliance by third parties with
++this License.
++
++  11. If, as a consequence of a court judgment or allegation of patent
++infringement or for any other reason (not limited to patent issues),
++conditions are imposed on you (whether by court order, agreement or
++otherwise) that contradict the conditions of this License, they do not
++excuse you from the conditions of this License.  If you cannot
++distribute so as to satisfy simultaneously your obligations under this
++License and any other pertinent obligations, then as a consequence you
++may not distribute the Library at all.  For example, if a patent
++license would not permit royalty-free redistribution of the Library by
++all those who receive copies directly or indirectly through you, then
++the only way you could satisfy both it and this License would be to
++refrain entirely from distribution of the Library.
++
++If any portion of this section is held invalid or unenforceable under any
++particular circumstance, the balance of the section is intended to apply,
++and the section as a whole is intended to apply in other circumstances.
++
++It is not the purpose of this section to induce you to infringe any
++patents or other property right claims or to contest validity of any
++such claims; this section has the sole purpose of protecting the
++integrity of the free software distribution system which is
++implemented by public license practices.  Many people have made
++generous contributions to the wide range of software distributed
++through that system in reliance on consistent application of that
++system; it is up to the author/donor to decide if he or she is willing
++to distribute software through any other system and a licensee cannot
++impose that choice.
++
++This section is intended to make thoroughly clear what is believed to
++be a consequence of the rest of this License.
++
++  12. If the distribution and/or use of the Library is restricted in
++certain countries either by patents or by copyrighted interfaces, the
++original copyright holder who places the Library under this License may add
++an explicit geographical distribution limitation excluding those countries,
++so that distribution is permitted only in or among countries not thus
++excluded.  In such case, this License incorporates the limitation as if
++written in the body of this License.
++
++  13. The Free Software Foundation may publish revised and/or new
++versions of the Lesser General Public License from time to time.
++Such new versions will be similar in spirit to the present version,
++but may differ in detail to address new problems or concerns.
++
++Each version is given a distinguishing version number.  If the Library
++specifies a version number of this License which applies to it and
++"any later version", you have the option of following the terms and
++conditions either of that version or of any later version published by
++the Free Software Foundation.  If the Library does not specify a
++license version number, you may choose any version ever published by
++the Free Software Foundation.
++
++  14. If you wish to incorporate parts of the Library into other free
++programs whose distribution conditions are incompatible with these,
++write to the author to ask for permission.  For software which is
++copyrighted by the Free Software Foundation, write to the Free
++Software Foundation; we sometimes make exceptions for this.  Our
++decision will be guided by the two goals of preserving the free status
++of all derivatives of our free software and of promoting the sharing
++and reuse of software generally.
++
++			    NO WARRANTY
++
++  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
++WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
++EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
++OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
++KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
++PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
++LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
++THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
++
++  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
++WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
++AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
++FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
++CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
++LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
++RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
++FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
++SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
++DAMAGES.
++
++		     END OF TERMS AND CONDITIONS
++
++           How to Apply These Terms to Your New Libraries
++
++  If you develop a new library, and you want it to be of the greatest
++possible use to the public, we recommend making it free software that
++everyone can redistribute and change.  You can do so by permitting
++redistribution under these terms (or, alternatively, under the terms of the
++ordinary General Public License).
++
++  To apply these terms, attach the following notices to the library.  It is
++safest to attach them to the start of each source file to most effectively
++convey the exclusion of warranty; and each file should have at least the
++"copyright" line and a pointer to where the full notice is found.
++
++    <one line to give the library's name and a brief idea of what it does.>
++    Copyright (C) <year>  <name of author>
++
++    This library is free software; you can redistribute it and/or
++    modify it under the terms of the GNU Lesser General Public
++    License as published by the Free Software Foundation; either
++    version 2.1 of the License, or (at your option) any later version.
++
++    This library 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
++    Lesser General Public License for more details.
++
++    You should have received a copy of the GNU Lesser General Public
++    License along with this library; if not, write to the Free Software
++    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++
++Also add information on how to contact you by electronic and paper mail.
++
++You should also get your employer (if you work as a programmer) or your
++school, if any, to sign a "copyright disclaimer" for the library, if
++necessary.  Here is a sample; alter the names:
++
++  Yoyodyne, Inc., hereby disclaims all copyright interest in the
++  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
++
++  <signature of Ty Coon>, 1 April 1990
++  Ty Coon, President of Vice
++
++That's all there is to it!
++
++
+diff --git a/utils/open-isns/ChangeLog b/utils/open-isns/ChangeLog
+new file mode 100644
+index 0000000..36d6506
+--- /dev/null
++++ b/utils/open-isns/ChangeLog
+@@ -0,0 +1,50 @@
++Under development:
++
++2007-09-27:
++	Fixed a serious interoperability bug
++	Added SLP support (using openslp)
++	Init script (courtesy Albert Pauw)
++
++2007-09-18:
++	Fixed a number of bugs
++	Added more test cases
++	Implemented default DD
++	Support autoconf, and building with/without openssl
++
++2007-08-24:
++	Improved discovery domain handling
++	Implemented DD deregistration
++	Backward compat fixes for older openssl versions
++	Made SCN more robust, SCN state now persists across server restarts
++	More regression tests
++
++2007-07-27:
++	Implemented SCN and ESI
++	Created iSNS discovery daemon (isnsdd)
++	Rewrote the policy handling a bit
++	Started to write some regression test code
++	Better manpages
++
++2007-07-12:
++	DevGetNext support
++	You can now define policies linking authentication
++		to permitted storage node names, permitted
++		entity names, etc.
++	Implemented DDReg
++	Queries and GetNext are now scoped to discovery domains
++	Lots of little bits and pieces for RFC conformance
++
++2005-07-18:
++	Public snapshot released
++	DSA based authentication
++	Deregistration
++	Simple file backed storage for the iSNS database
++	Entity Registration Period + Timestamp support (server side),
++		and entity expiration
++	isnsd now writes a pid file
++	Improved manual pages
++	DevGetNext support under development
++
++2007-05-11:
++	First public release, supporting register/query
++
+diff --git a/utils/open-isns/HACKING b/utils/open-isns/HACKING
+new file mode 100644
+index 0000000..95b330c
+--- /dev/null
++++ b/utils/open-isns/HACKING
+@@ -0,0 +1,30 @@
++
++When hacking on open-isns, or when trying to locate a problem,
++the following information may be useful:
++
++ -	You can start the daemon using the -f option, which
++ 	prevents it from backgrounding itself. Crucial if
++	you want to run it in a debugger, or under strace.
++
++	This option works for isnsd and isnsdd
++
++ -	All tools support the "-d" option to enable debugging.
++ 	In general, you want to use "-d all" to turn on all
++	debugging options. However, you can select individual
++	debug facilities - check out the manpages and/or
++	the source code in logging.c
++
++ -	If isnsd crashes, and you suspect memory corruption,
++	you can compile open-isns with memory debugging enabled.  Re-run
++	the configure script and add the option --enable-memdebug. Then
++	run "make clean all" to rebuild everything.
++
++	Memory debugging can be chosen at run-time by setting the
++	ISNS_MDEBUG environment variable, and re-starting the application:
++
++	export ISNS_MDEBUG=1
++	./isnsd -f -d all
++
++	Memory debugging works for all memory allocations done by the
++	Open-iSNS code, but does not affect memory allocations by other
++	libraries (such as glibc or openssl).
+diff --git a/utils/open-isns/Makefile.in b/utils/open-isns/Makefile.in
+new file mode 100644
+index 0000000..a27199c
+--- /dev/null
++++ b/utils/open-isns/Makefile.in
+@@ -0,0 +1,81 @@
++prefix	= @prefix@
++exec_prefix = @exec_prefix@
++sbindir	= @sbindir@
++mandir	= @mandir@
++etcdir	= /etc
++vardir	= /var/lib/isns
++
++SBINDIR	= $(INSTALL_ROOT)$(sbindir)
++ETCDIR	= $(INSTALL_ROOT)$(etcdir)
++CFGDIR	= $(ETCDIR)/isns
++MANDIR	= $(INSTALL_ROOT)$(mandir)
++VARDIR	= $(INSTALL_ROOT)$(vardir)
++
++CC	= @CC@
++CPPFLAGS= @CPPFLAGS@
++CFLAGS	= @CFLAGS@ -I.
++LDFLAGS	= @LDFLAGS@
++
++LIB	= libisns.a
++LIBOBJS	= server.o \
++	  client.o \
++	  objects.o \
++	  callback.o \
++	  timer.o \
++	  vendor.o \
++	  db.o \
++	  db-file.o \
++	  db-policy.o \
++	  relation.o \
++	  scope.o \
++	  message.o \
++	  security.o \
++	  authblock.o \
++	  policy.o \
++	  register.o \
++	  query.o \
++	  getnext.o \
++	  deregister.o \
++	  esi.o \
++	  scn.o \
++	  dd.o \
++	  entity.o \
++	  portal-group.o \
++	  storage-node.o \
++	  domain.o \
++	  simple.o \
++	  tags.o \
++	  attrs.o \
++	  export.o \
++	  socket.o \
++	  slp.o \
++	  error.o \
++	  logging.o \
++	  config.o \
++	  parser.o \
++	  buffer.o \
++	  pidfile.o \
++	  sysdep-unix.o \
++	  util.o \
++	  bitvector.o \
++	  mdebug.o
++SECLINK	= @SECLIBS@
++SLPLINK	= @SLPLIBS@
++SLPLIN	= @SLPLIBS@
++
++all: $(LIB)
++
++clean distclean::
++	rm -f *.o $(LIB) *~
++
++distclean::
++	rm -f config.h Makefile config.status config.log
++	rm -rf autom4te.cache
++
++$(LIB): $(LIBOBJS)
++	ar cr $@ $(LIBOBJS)
++
++depend:
++	gcc $(CFLAGS) -M `ls *.c` > .depend
++
++-include .depend
+diff --git a/utils/open-isns/README b/utils/open-isns/README
+new file mode 100644
+index 0000000..acff29b
+--- /dev/null
++++ b/utils/open-isns/README
+@@ -0,0 +1,173 @@
++
++Welcome to Open-iSNS
++====================
++
++This is a partial implementation of iSNS, according to RFC4171.
++The implementation is still somewhat incomplete, but I'm releasing
++it for your reading pleasure.
++
++The distribution comprises
++
++ isnsd
++ 	This is the iSNS server, supporting persistent storage
++	of registrations in a file based database.
++
++ isnsadm
++ 	A command line utility for querying the iSNS database,
++	and for registering/deregistering nodes and portals
++
++ isnsdd
++ 	An iSNS Discovery Daemon, which is still very much work
++	in progress. The daemon is supposed to handle all the
++	bit banging and server communications required to register
++	a node, its portals, and to maintain the registration.
++	It is also supposed to use the iSNS State Change Notification
++	framework to learn of new targets or initiators coming online,
++	and inform local services (such as the iSCSI initiator daemon)
++	about these changes.
++
++Thanks!
++-------
++
++Many thanks to Albert Pauw for his fearless testing of snapshots,
++and his copious feedback!
++
++What works, after a fashion:
++----------------------------
++
++ -	For now, I've been focusing on getting the iSCSI part to
++ 	work. There is some very basic support for FC objects, but
++	this will be hardly useful yet.
++
++ -	Registration, deregistration, query, getnext
++	You can use isnsadm to register iSCSI nodes, and portals.
++	isnsadm also illustrates how this is supposed to be used from
++	the client perspective.
++
++ -	Discovery domains are supported mostly. The administrator
++ 	can create discovery domains using isnsadm, and place storage
++	nodes in domains. Queries by clients are scoped by their
++	discovery domains membership, so that they will be unable to
++	see nodes not part of a shared DD.
++
++	Open-iSNS currently does not allow clients to place themselves
++	in a DD.
++
++	Optionally, storage nodes that are not in any discovery domain
++	will be placed in a "default DD" (see the DefaultDiscoveryDomain
++	in isnsd.conf).
++
++ -	ESI, supported both by the server and the discovery daemon
++
++ -	SCN, supported by the server and the discovery daemon
++
++What is still missing
++---------------------
++
++ -	Better documentation (esp. a HOWTO on getting started with iSNS)
++ -	DD Sets
++ -	Various bits and pieces of the protocol
++ -	FC support
++
++
++Building Open-iSNS
++------------------
++
++The Open-iSNS build is now based on autoconf. The distributed tarball
++should include a configure script and a config.h.in file generated
++from configure.ac. If these are missing, you can generate them
++by running
++
++	autoconf
++	autoheader
++
++For most people, it should be sufficient to run configure without any
++arguments, or at most with the option --prefix. If run without --prefix,
++program files, manpages etc will be installed below /usr/local. To have
++everything installed /usr/bin, /usr/share/man etc, run it as
++
++	./configure --prefix=/usr
++
++Dependencies:
++
++ -	If you want to build Open-iSNS with support for authentication,
++ 	you need the OpenSSL libraries and header files installed.
++
++	The configure script should pick up the presence of these
++	libraries, and enable security support automatically. To disable
++	this explicitly in your build, pass the --without-security option
++	to configure.
++
++ -	If you want to build Open-iSNS with SLP support, you need the
++ 	OpenSLP library and header file installed.
++
++	The configure script should pick up the presence of this library,
++	and enable SLP support automatically. To disable this explicitly
++	in your build, pass the --without-slp option to configure.
++
++When configure is run, it checks for a the presence of a number of
++headers and libraries in your system (the results of most of these checks
++are currently ignored :-). Then, it creates a Makefile and a config.h
++include file. With these in place, you can build the binaries and libraries:
++
++	make
++	make install
++
++Getting started
++---------------
++
++On the iSNS server, you need to generate a server key and install it. The
++simplest way is probably to use the isnssetup script included in the
++source package.
++
++For each client you wish to use, you should then 
++
++iSNS Security
++-------------
++
++This implementation of iSNS supports authentication, as descibed in RFC
++4171. In order to use it, you have to create DSA keys for the server and
++all clients.
++
++iSNS uses conceptually the same security mechanism as SLP, and identifies
++principals by a "Security Parameter Index", which is essentially a string
++identifying a key.
++
++Open-iSNS fully supports DSA based security, and offers a flexible
++policy mechanism that ties an SPI to a network entity and the storage
++node names it is allowed to use. For an introduction to the security
++model used by Open-iSNS, refer to the isns_config(5) manual page. An
++overview on setting up the iSNS server for authentication is given in
++the EXAMPLES section of the isnsadm(8) manual page.
++
++Downloading Open-iSNS
++---------------------
++
++Open-iSNS is available for download from
++
++	http://oss.oracle.com/~okir/open-isns
++
++You have to grab the latest tarball and compile it; fancy things such
++as RPMs are not available yet.
++
++------------------------------------------------------------------
++
++
++			COPYRIGHT NOTICE
++
++	Copyright (C) 2007 Olaf Kirch.
++
++	This library is free software; you can redistribute it and/or
++	modify it under the terms of the GNU Lesser General Public
++	License as published by the Free Software Foundation; either
++	version 2.1 of the License, or (at your option) any later version.
++
++	This library 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
++	Lesser General Public License for more details.
++
++	You should have received a copy of the GNU Lesser General Public
++	License along with this library; if not, write to the Free Software
++	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++
+diff --git a/utils/open-isns/TODO b/utils/open-isns/TODO
+new file mode 100644
+index 0000000..2ddf008
+--- /dev/null
++++ b/utils/open-isns/TODO
+@@ -0,0 +1,100 @@
++Documentation:
++ -	Add HOWTO
++
++isnsd:
++ -	When registering a node, use the default EID given in its
++ 	policy (avoid the isns.control trap)
++ -	make PGs children of the iSCSI storage node they're associated
++ 	with?
++ -	Implement missing functions
++
++isnsadm:
++ -	support iSNS server discovery through DNS SRV
++ 	records, and SLP
++
++isnsdd:
++ -	support iSNS server discovery through DNS SRV
++ 	records, and SLP
++ -	At startup, query the server for the list of
++ 	visible nodes/portals
++ -	When receiving an SCN, query for the node's
++ 	portals, authmethod and such, and compare that
++	to what we have cached
++ -	At regular intervals, repeat the query for
++ 	all visible nodes/portals, and do a diff with
++	our shadow DB
++ -	At regular intervals, check whether the portals
++ 	we registered for ESI are seeing the server's
++	ESI messages.
++
++DevAttrReg:
++ -	Refuse registration of nodes inside the CONTROL
++ 	entity, unless it's a control node.
++ -	If the client uses REPLACE, is it okay for the
++ 	entity's index to change?
++ -	security: optionally validate the IP addresses
++ 	a client registers (either against a static policy,
++	or using DNS).
++ -	relaxed security model: require privilege
++ 	for registration of targets; anyone can register
++	an initiator?
++ -	Gracefully handle registrations where the client
++	specifies an index attribute, as long as it matches
++	the next_index
++
++DevAttrQuery:
++ -	fix --local --query policy-index=iqn.1969-12.brummo
++	and write test case
++ -	fix the way we enumerate related objects
++ -	ensure DD discovery works (5.6.5.2):
++	   DD membership can be discovered through the DevAttrQry message
++	   by including either DD member attributes (i.e., DD Member
++	   iSCSI Index, DD Member iSCSI Node, DD Member iFCP Node, DD
++	   Member Portal Index, DD Member Portal IP Addr, and DD Member
++	   Portal TCP/UDP) or the object key of the Storage Node or
++	   Portal (i.e., iSCSI Name, iSCSI Index, Portal IP Addr, Portal
++	   TCP/UDP Port, and Portal Index) in the Operating Attributes.
++	   Using DD member attributes SHALL return both registered and
++	   unregistered member Storage Nodes and/or Portals of a DD.
++	   DevAttrQry messages using the Storage Node and/or Portal
++	   object key SHALL return only member Storage Nodes or Portals
++	   that are currently registered in the iSNS database.
++
++DevAttrDereg:
++ -	PG Removal code: ignore nodes/portal that are dead
++ -	review security
++ -	cancel any SCN/ESI callbacks
++
++SCN:
++ -	Trigger a mgmt reg SCN when accepting a mgmt registration
++
++SCNEvent:
++ -	Implement
++
++ESI:
++ -	Right now the way we re-establish ESI state after database
++ 	reload is awkward.
++
++DDReg:
++ -	Write test cases
++
++DDDereg:
++ -	Write test cases
++
++DDSReg/DDSDereg:
++ -	Implement
++
++Heartbeat:
++ -	Implement message send
++ -	Implement failover?
++
++Security:
++ -	Allow policies without key?
++ -	Implement simple default policies linking client IP +
++ 	hostname (network entity) + storage node names
++
++Renaming
++ -	Add isns_ prefix to all visible functions
++
++Socket code:
++ -	impose upper limit on the reassembly buffer
+diff --git a/utils/open-isns/aclocal/config.guess b/utils/open-isns/aclocal/config.guess
+new file mode 100644
+index 0000000..6d71f75
+--- /dev/null
++++ b/utils/open-isns/aclocal/config.guess
+@@ -0,0 +1,1499 @@
++#! /bin/sh
++# Attempt to guess a canonical system name.
++#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
++#   2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
++
++timestamp='2005-05-27'
++
++# This file 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 2 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, write to the Free Software
++# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
++# 02110-1301, USA.
++#
++# As a special exception to the GNU General Public License, if you
++# distribute this file as part of a program that contains a
++# configuration script generated by Autoconf, you may include it under
++# the same distribution terms that you use for the rest of that program.
++
++
++# Originally written by Per Bothner <per@bothner.com>.
++# Please send patches to <config-patches@gnu.org>.  Submit a context
++# diff and a properly formatted ChangeLog entry.
++#
++# This script attempts to guess a canonical system name similar to
++# config.sub.  If it succeeds, it prints the system name on stdout, and
++# exits with 0.  Otherwise, it exits with 1.
++#
++# The plan is that this can be called by configure scripts if you
++# don't specify an explicit build system type.
++
++me=`echo "$0" | sed -e 's,.*/,,'`
++
++usage="\
++Usage: $0 [OPTION]
++
++Output the configuration name of the system \`$me' is run on.
++
++Operation modes:
++  -h, --help         print this help, then exit
++  -t, --time-stamp   print date of last modification, then exit
++  -v, --version      print version number, then exit
++
++Report bugs and patches to <config-patches@gnu.org>."
++
++version="\
++GNU config.guess ($timestamp)
++
++Originally written by Per Bothner.
++Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
++Free Software Foundation, Inc.
++
++This is free software; see the source for copying conditions.  There is NO
++warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
++
++help="
++Try \`$me --help' for more information."
++
++# Parse command line
++while test $# -gt 0 ; do
++  case $1 in
++    --time-stamp | --time* | -t )
++       echo "$timestamp" ; exit ;;
++    --version | -v )
++       echo "$version" ; exit ;;
++    --help | --h* | -h )
++       echo "$usage"; exit ;;
++    -- )     # Stop option processing
++       shift; break ;;
++    - )	# Use stdin as input.
++       break ;;
++    -* )
++       echo "$me: invalid option $1$help" >&2
++       exit 1 ;;
++    * )
++       break ;;
++  esac
++done
++
++if test $# != 0; then
++  echo "$me: too many arguments$help" >&2
++  exit 1
++fi
++
++trap 'exit 1' 1 2 15
++
++# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
++# compiler to aid in system detection is discouraged as it requires
++# temporary files to be created and, as you can see below, it is a
++# headache to deal with in a portable fashion.
++
++# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
++# use `HOST_CC' if defined, but it is deprecated.
++
++# Portable tmp directory creation inspired by the Autoconf team.
++
++set_cc_for_build='
++trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
++trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
++: ${TMPDIR=/tmp} ;
++ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
++ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
++ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
++ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
++dummy=$tmp/dummy ;
++tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
++case $CC_FOR_BUILD,$HOST_CC,$CC in
++ ,,)    echo "int x;" > $dummy.c ;
++	for c in cc gcc c89 c99 ; do
++	  if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
++	     CC_FOR_BUILD="$c"; break ;
++	  fi ;
++	done ;
++	if test x"$CC_FOR_BUILD" = x ; then
++	  CC_FOR_BUILD=no_compiler_found ;
++	fi
++	;;
++ ,,*)   CC_FOR_BUILD=$CC ;;
++ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
++esac ;'
++
++# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
++# (ghazi@noc.rutgers.edu 1994-08-24)
++if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
++	PATH=$PATH:/.attbin ; export PATH
++fi
++
++UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
++UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
++UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
++UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
++
++# Note: order is significant - the case branches are not exclusive.
++
++case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
++    *:NetBSD:*:*)
++	# NetBSD (nbsd) targets should (where applicable) match one or
++	# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
++	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
++	# switched to ELF, *-*-netbsd* would select the old
++	# object file format.  This provides both forward
++	# compatibility and a consistent mechanism for selecting the
++	# object file format.
++	#
++	# Note: NetBSD doesn't particularly care about the vendor
++	# portion of the name.  We always set it to "unknown".
++	sysctl="sysctl -n hw.machine_arch"
++	UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
++	    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
++	case "${UNAME_MACHINE_ARCH}" in
++	    armeb) machine=armeb-unknown ;;
++	    arm*) machine=arm-unknown ;;
++	    sh3el) machine=shl-unknown ;;
++	    sh3eb) machine=sh-unknown ;;
++	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
++	esac
++	# The Operating System including object format, if it has switched
++	# to ELF recently, or will in the future.
++	case "${UNAME_MACHINE_ARCH}" in
++	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
++		eval $set_cc_for_build
++		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
++			| grep __ELF__ >/dev/null
++		then
++		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
++		    # Return netbsd for either.  FIX?
++		    os=netbsd
++		else
++		    os=netbsdelf
++		fi
++		;;
++	    *)
++	        os=netbsd
++		;;
++	esac
++	# The OS release
++	# Debian GNU/NetBSD machines have a different userland, and
++	# thus, need a distinct triplet. However, they do not need
++	# kernel version information, so it can be replaced with a
++	# suitable tag, in the style of linux-gnu.
++	case "${UNAME_VERSION}" in
++	    Debian*)
++		release='-gnu'
++		;;
++	    *)
++		release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
++		;;
++	esac
++	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
++	# contains redundant information, the shorter form:
++	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
++	echo "${machine}-${os}${release}"
++	exit ;;
++    amd64:OpenBSD:*:*)
++	echo x86_64-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    amiga:OpenBSD:*:*)
++	echo m68k-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    cats:OpenBSD:*:*)
++	echo arm-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    hp300:OpenBSD:*:*)
++	echo m68k-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    luna88k:OpenBSD:*:*)
++    	echo m88k-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    mac68k:OpenBSD:*:*)
++	echo m68k-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    macppc:OpenBSD:*:*)
++	echo powerpc-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    mvme68k:OpenBSD:*:*)
++	echo m68k-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    mvme88k:OpenBSD:*:*)
++	echo m88k-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    mvmeppc:OpenBSD:*:*)
++	echo powerpc-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    sgi:OpenBSD:*:*)
++	echo mips64-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    sun3:OpenBSD:*:*)
++	echo m68k-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    *:OpenBSD:*:*)
++	echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
++	exit ;;
++    *:ekkoBSD:*:*)
++	echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
++	exit ;;
++    macppc:MirBSD:*:*)
++	echo powerppc-unknown-mirbsd${UNAME_RELEASE}
++	exit ;;
++    *:MirBSD:*:*)
++	echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
++	exit ;;
++    alpha:OSF1:*:*)
++	case $UNAME_RELEASE in
++	*4.0)
++		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
++		;;
++	*5.*)
++	        UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
++		;;
++	esac
++	# According to Compaq, /usr/sbin/psrinfo has been available on
++	# OSF/1 and Tru64 systems produced since 1995.  I hope that
++	# covers most systems running today.  This code pipes the CPU
++	# types through head -n 1, so we only detect the type of CPU 0.
++	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
++	case "$ALPHA_CPU_TYPE" in
++	    "EV4 (21064)")
++		UNAME_MACHINE="alpha" ;;
++	    "EV4.5 (21064)")
++		UNAME_MACHINE="alpha" ;;
++	    "LCA4 (21066/21068)")
++		UNAME_MACHINE="alpha" ;;
++	    "EV5 (21164)")
++		UNAME_MACHINE="alphaev5" ;;
++	    "EV5.6 (21164A)")
++		UNAME_MACHINE="alphaev56" ;;
++	    "EV5.6 (21164PC)")
++		UNAME_MACHINE="alphapca56" ;;
++	    "EV5.7 (21164PC)")
++		UNAME_MACHINE="alphapca57" ;;
++	    "EV6 (21264)")
++		UNAME_MACHINE="alphaev6" ;;
++	    "EV6.7 (21264A)")
++		UNAME_MACHINE="alphaev67" ;;
++	    "EV6.8CB (21264C)")
++		UNAME_MACHINE="alphaev68" ;;
++	    "EV6.8AL (21264B)")
++		UNAME_MACHINE="alphaev68" ;;
++	    "EV6.8CX (21264D)")
++		UNAME_MACHINE="alphaev68" ;;
++	    "EV6.9A (21264/EV69A)")
++		UNAME_MACHINE="alphaev69" ;;
++	    "EV7 (21364)")
++		UNAME_MACHINE="alphaev7" ;;
++	    "EV7.9 (21364A)")
++		UNAME_MACHINE="alphaev79" ;;
++	esac
++	# A Pn.n version is a patched version.
++	# A Vn.n version is a released version.
++	# A Tn.n version is a released field test version.
++	# A Xn.n version is an unreleased experimental baselevel.
++	# 1.2 uses "1.2" for uname -r.
++	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
++	exit ;;
++    Alpha\ *:Windows_NT*:*)
++	# How do we know it's Interix rather than the generic POSIX subsystem?
++	# Should we change UNAME_MACHINE based on the output of uname instead
++	# of the specific Alpha model?
++	echo alpha-pc-interix
++	exit ;;
++    21064:Windows_NT:50:3)
++	echo alpha-dec-winnt3.5
++	exit ;;
++    Amiga*:UNIX_System_V:4.0:*)
++	echo m68k-unknown-sysv4
++	exit ;;
++    *:[Aa]miga[Oo][Ss]:*:*)
++	echo ${UNAME_MACHINE}-unknown-amigaos
++	exit ;;
++    *:[Mm]orph[Oo][Ss]:*:*)
++	echo ${UNAME_MACHINE}-unknown-morphos
++	exit ;;
++    *:OS/390:*:*)
++	echo i370-ibm-openedition
++	exit ;;
++    *:z/VM:*:*)
++	echo s390-ibm-zvmoe
++	exit ;;
++    *:OS400:*:*)
++        echo powerpc-ibm-os400
++	exit ;;
++    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
++	echo arm-acorn-riscix${UNAME_RELEASE}
++	exit ;;
++    arm:riscos:*:*|arm:RISCOS:*:*)
++	echo arm-unknown-riscos
++	exit ;;
++    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
++	echo hppa1.1-hitachi-hiuxmpp
++	exit ;;
++    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
++	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
++	if test "`(/bin/universe) 2>/dev/null`" = att ; then
++		echo pyramid-pyramid-sysv3
++	else
++		echo pyramid-pyramid-bsd
++	fi
++	exit ;;
++    NILE*:*:*:dcosx)
++	echo pyramid-pyramid-svr4
++	exit ;;
++    DRS?6000:unix:4.0:6*)
++	echo sparc-icl-nx6
++	exit ;;
++    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
++	case `/usr/bin/uname -p` in
++	    sparc) echo sparc-icl-nx7; exit ;;
++	esac ;;
++    sun4H:SunOS:5.*:*)
++	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
++	exit ;;
++    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
++	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
++	exit ;;
++    i86pc:SunOS:5.*:*)
++	echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
++	exit ;;
++    sun4*:SunOS:6*:*)
++	# According to config.sub, this is the proper way to canonicalize
++	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
++	# it's likely to be more like Solaris than SunOS4.
++	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
++	exit ;;
++    sun4*:SunOS:*:*)
++	case "`/usr/bin/arch -k`" in
++	    Series*|S4*)
++		UNAME_RELEASE=`uname -v`
++		;;
++	esac
++	# Japanese Language versions have a version number like `4.1.3-JL'.
++	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
++	exit ;;
++    sun3*:SunOS:*:*)
++	echo m68k-sun-sunos${UNAME_RELEASE}
++	exit ;;
++    sun*:*:4.2BSD:*)
++	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
++	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
++	case "`/bin/arch`" in
++	    sun3)
++		echo m68k-sun-sunos${UNAME_RELEASE}
++		;;
++	    sun4)
++		echo sparc-sun-sunos${UNAME_RELEASE}
++		;;
++	esac
++	exit ;;
++    aushp:SunOS:*:*)
++	echo sparc-auspex-sunos${UNAME_RELEASE}
++	exit ;;
++    # The situation for MiNT is a little confusing.  The machine name
++    # can be virtually everything (everything which is not
++    # "atarist" or "atariste" at least should have a processor
++    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
++    # to the lowercase version "mint" (or "freemint").  Finally
++    # the system name "TOS" denotes a system which is actually not
++    # MiNT.  But MiNT is downward compatible to TOS, so this should
++    # be no problem.
++    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
++        echo m68k-atari-mint${UNAME_RELEASE}
++	exit ;;
++    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
++	echo m68k-atari-mint${UNAME_RELEASE}
++        exit ;;
++    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
++        echo m68k-atari-mint${UNAME_RELEASE}
++	exit ;;
++    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
++        echo m68k-milan-mint${UNAME_RELEASE}
++        exit ;;
++    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
++        echo m68k-hades-mint${UNAME_RELEASE}
++        exit ;;
++    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
++        echo m68k-unknown-mint${UNAME_RELEASE}
++        exit ;;
++    m68k:machten:*:*)
++	echo m68k-apple-machten${UNAME_RELEASE}
++	exit ;;
++    powerpc:machten:*:*)
++	echo powerpc-apple-machten${UNAME_RELEASE}
++	exit ;;
++    RISC*:Mach:*:*)
++	echo mips-dec-mach_bsd4.3
++	exit ;;
++    RISC*:ULTRIX:*:*)
++	echo mips-dec-ultrix${UNAME_RELEASE}
++	exit ;;
++    VAX*:ULTRIX*:*:*)
++	echo vax-dec-ultrix${UNAME_RELEASE}
++	exit ;;
++    2020:CLIX:*:* | 2430:CLIX:*:*)
++	echo clipper-intergraph-clix${UNAME_RELEASE}
++	exit ;;
++    mips:*:*:UMIPS | mips:*:*:RISCos)
++	eval $set_cc_for_build
++	sed 's/^	//' << EOF >$dummy.c
++#ifdef __cplusplus
++#include <stdio.h>  /* for printf() prototype */
++	int main (int argc, char *argv[]) {
++#else
++	int main (argc, argv) int argc; char *argv[]; {
++#endif
++	#if defined (host_mips) && defined (MIPSEB)
++	#if defined (SYSTYPE_SYSV)
++	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
++	#endif
++	#if defined (SYSTYPE_SVR4)
++	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
++	#endif
++	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
++	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
++	#endif
++	#endif
++	  exit (-1);
++	}
++EOF
++	$CC_FOR_BUILD -o $dummy $dummy.c &&
++	  dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
++	  SYSTEM_NAME=`$dummy $dummyarg` &&
++	    { echo "$SYSTEM_NAME"; exit; }
++	echo mips-mips-riscos${UNAME_RELEASE}
++	exit ;;
++    Motorola:PowerMAX_OS:*:*)
++	echo powerpc-motorola-powermax
++	exit ;;
++    Motorola:*:4.3:PL8-*)
++	echo powerpc-harris-powermax
++	exit ;;
++    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
++	echo powerpc-harris-powermax
++	exit ;;
++    Night_Hawk:Power_UNIX:*:*)
++	echo powerpc-harris-powerunix
++	exit ;;
++    m88k:CX/UX:7*:*)
++	echo m88k-harris-cxux7
++	exit ;;
++    m88k:*:4*:R4*)
++	echo m88k-motorola-sysv4
++	exit ;;
++    m88k:*:3*:R3*)
++	echo m88k-motorola-sysv3
++	exit ;;
++    AViiON:dgux:*:*)
++        # DG/UX returns AViiON for all architectures
++        UNAME_PROCESSOR=`/usr/bin/uname -p`
++	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
++	then
++	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
++	       [ ${TARGET_BINARY_INTERFACE}x = x ]
++	    then
++		echo m88k-dg-dgux${UNAME_RELEASE}
++	    else
++		echo m88k-dg-dguxbcs${UNAME_RELEASE}
++	    fi
++	else
++	    echo i586-dg-dgux${UNAME_RELEASE}
++	fi
++ 	exit ;;
++    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
++	echo m88k-dolphin-sysv3
++	exit ;;
++    M88*:*:R3*:*)
++	# Delta 88k system running SVR3
++	echo m88k-motorola-sysv3
++	exit ;;
++    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
++	echo m88k-tektronix-sysv3
++	exit ;;
++    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
++	echo m68k-tektronix-bsd
++	exit ;;
++    *:IRIX*:*:*)
++	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
++	exit ;;
++    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
++	echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
++	exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
++    i*86:AIX:*:*)
++	echo i386-ibm-aix
++	exit ;;
++    ia64:AIX:*:*)
++	if [ -x /usr/bin/oslevel ] ; then
++		IBM_REV=`/usr/bin/oslevel`
++	else
++		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
++	fi
++	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
++	exit ;;
++    *:AIX:2:3)
++	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
++		eval $set_cc_for_build
++		sed 's/^		//' << EOF >$dummy.c
++		#include <sys/systemcfg.h>
++
++		main()
++			{
++			if (!__power_pc())
++				exit(1);
++			puts("powerpc-ibm-aix3.2.5");
++			exit(0);
++			}
++EOF
++		if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
++		then
++			echo "$SYSTEM_NAME"
++		else
++			echo rs6000-ibm-aix3.2.5
++		fi
++	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
++		echo rs6000-ibm-aix3.2.4
++	else
++		echo rs6000-ibm-aix3.2
++	fi
++	exit ;;
++    *:AIX:*:[45])
++	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
++	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
++		IBM_ARCH=rs6000
++	else
++		IBM_ARCH=powerpc
++	fi
++	if [ -x /usr/bin/oslevel ] ; then
++		IBM_REV=`/usr/bin/oslevel`
++	else
++		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
++	fi
++	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
++	exit ;;
++    *:AIX:*:*)
++	echo rs6000-ibm-aix
++	exit ;;
++    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
++	echo romp-ibm-bsd4.4
++	exit ;;
++    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
++	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
++	exit ;;                             # report: romp-ibm BSD 4.3
++    *:BOSX:*:*)
++	echo rs6000-bull-bosx
++	exit ;;
++    DPX/2?00:B.O.S.:*:*)
++	echo m68k-bull-sysv3
++	exit ;;
++    9000/[34]??:4.3bsd:1.*:*)
++	echo m68k-hp-bsd
++	exit ;;
++    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
++	echo m68k-hp-bsd4.4
++	exit ;;
++    9000/[34678]??:HP-UX:*:*)
++	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
++	case "${UNAME_MACHINE}" in
++	    9000/31? )            HP_ARCH=m68000 ;;
++	    9000/[34]?? )         HP_ARCH=m68k ;;
++	    9000/[678][0-9][0-9])
++		if [ -x /usr/bin/getconf ]; then
++		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
++                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
++                    case "${sc_cpu_version}" in
++                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
++                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
++                      532)                      # CPU_PA_RISC2_0
++                        case "${sc_kernel_bits}" in
++                          32) HP_ARCH="hppa2.0n" ;;
++                          64) HP_ARCH="hppa2.0w" ;;
++			  '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
++                        esac ;;
++                    esac
++		fi
++		if [ "${HP_ARCH}" = "" ]; then
++		    eval $set_cc_for_build
++		    sed 's/^              //' << EOF >$dummy.c
++
++              #define _HPUX_SOURCE
++              #include <stdlib.h>
++              #include <unistd.h>
++
++              int main ()
++              {
++              #if defined(_SC_KERNEL_BITS)
++                  long bits = sysconf(_SC_KERNEL_BITS);
++              #endif
++                  long cpu  = sysconf (_SC_CPU_VERSION);
++
++                  switch (cpu)
++              	{
++              	case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
++              	case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
++              	case CPU_PA_RISC2_0:
++              #if defined(_SC_KERNEL_BITS)
++              	    switch (bits)
++              		{
++              		case 64: puts ("hppa2.0w"); break;
++              		case 32: puts ("hppa2.0n"); break;
++              		default: puts ("hppa2.0"); break;
++              		} break;
++              #else  /* !defined(_SC_KERNEL_BITS) */
++              	    puts ("hppa2.0"); break;
++              #endif
++              	default: puts ("hppa1.0"); break;
++              	}
++                  exit (0);
++              }
++EOF
++		    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
++		    test -z "$HP_ARCH" && HP_ARCH=hppa
++		fi ;;
++	esac
++	if [ ${HP_ARCH} = "hppa2.0w" ]
++	then
++	    # avoid double evaluation of $set_cc_for_build
++	    test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
++
++	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
++	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
++	    # generating 64-bit code.  GNU and HP use different nomenclature:
++	    #
++	    # $ CC_FOR_BUILD=cc ./config.guess
++	    # => hppa2.0w-hp-hpux11.23
++	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
++	    # => hppa64-hp-hpux11.23
++
++	    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
++		grep __LP64__ >/dev/null
++	    then
++		HP_ARCH="hppa2.0w"
++	    else
++		HP_ARCH="hppa64"
++	    fi
++	fi
++	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
++	exit ;;
++    ia64:HP-UX:*:*)
++	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
++	echo ia64-hp-hpux${HPUX_REV}
++	exit ;;
++    3050*:HI-UX:*:*)
++	eval $set_cc_for_build
++	sed 's/^	//' << EOF >$dummy.c
++	#include <unistd.h>
++	int
++	main ()
++	{
++	  long cpu = sysconf (_SC_CPU_VERSION);
++	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
++	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
++	     results, however.  */
++	  if (CPU_IS_PA_RISC (cpu))
++	    {
++	      switch (cpu)
++		{
++		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
++		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
++		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
++		  default: puts ("hppa-hitachi-hiuxwe2"); break;
++		}
++	    }
++	  else if (CPU_IS_HP_MC68K (cpu))
++	    puts ("m68k-hitachi-hiuxwe2");
++	  else puts ("unknown-hitachi-hiuxwe2");
++	  exit (0);
++	}
++EOF
++	$CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
++		{ echo "$SYSTEM_NAME"; exit; }
++	echo unknown-hitachi-hiuxwe2
++	exit ;;
++    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
++	echo hppa1.1-hp-bsd
++	exit ;;
++    9000/8??:4.3bsd:*:*)
++	echo hppa1.0-hp-bsd
++	exit ;;
++    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
++	echo hppa1.0-hp-mpeix
++	exit ;;
++    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
++	echo hppa1.1-hp-osf
++	exit ;;
++    hp8??:OSF1:*:*)
++	echo hppa1.0-hp-osf
++	exit ;;
++    i*86:OSF1:*:*)
++	if [ -x /usr/sbin/sysversion ] ; then
++	    echo ${UNAME_MACHINE}-unknown-osf1mk
++	else
++	    echo ${UNAME_MACHINE}-unknown-osf1
++	fi
++	exit ;;
++    parisc*:Lites*:*:*)
++	echo hppa1.1-hp-lites
++	exit ;;
++    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
++	echo c1-convex-bsd
++        exit ;;
++    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
++	if getsysinfo -f scalar_acc
++	then echo c32-convex-bsd
++	else echo c2-convex-bsd
++	fi
++        exit ;;
++    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
++	echo c34-convex-bsd
++        exit ;;
++    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
++	echo c38-convex-bsd
++        exit ;;
++    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
++	echo c4-convex-bsd
++        exit ;;
++    CRAY*Y-MP:*:*:*)
++	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
++	exit ;;
++    CRAY*[A-Z]90:*:*:*)
++	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
++	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
++	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
++	      -e 's/\.[^.]*$/.X/'
++	exit ;;
++    CRAY*TS:*:*:*)
++	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
++	exit ;;
++    CRAY*T3E:*:*:*)
++	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
++	exit ;;
++    CRAY*SV1:*:*:*)
++	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
++	exit ;;
++    *:UNICOS/mp:*:*)
++	echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
++	exit ;;
++    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
++	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
++        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
++        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
++        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
++        exit ;;
++    5000:UNIX_System_V:4.*:*)
++        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
++        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
++        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
++	exit ;;
++    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
++	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
++	exit ;;
++    sparc*:BSD/OS:*:*)
++	echo sparc-unknown-bsdi${UNAME_RELEASE}
++	exit ;;
++    *:BSD/OS:*:*)
++	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
++	exit ;;
++    *:FreeBSD:*:*)
++	echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
++	exit ;;
++    i*:CYGWIN*:*)
++	echo ${UNAME_MACHINE}-pc-cygwin
++	exit ;;
++    i*:MINGW*:*)
++	echo ${UNAME_MACHINE}-pc-mingw32
++	exit ;;
++    i*:windows32*:*)
++    	# uname -m includes "-pc" on this system.
++    	echo ${UNAME_MACHINE}-mingw32
++	exit ;;
++    i*:PW*:*)
++	echo ${UNAME_MACHINE}-pc-pw32
++	exit ;;
++    x86:Interix*:[34]*)
++	echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
++	exit ;;
++    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
++	echo i${UNAME_MACHINE}-pc-mks
++	exit ;;
++    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
++	# How do we know it's Interix rather than the generic POSIX subsystem?
++	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
++	# UNAME_MACHINE based on the output of uname instead of i386?
++	echo i586-pc-interix
++	exit ;;
++    i*:UWIN*:*)
++	echo ${UNAME_MACHINE}-pc-uwin
++	exit ;;
++    amd64:CYGWIN*:*:*)
++	echo x86_64-unknown-cygwin
++	exit ;;
++    p*:CYGWIN*:*)
++	echo powerpcle-unknown-cygwin
++	exit ;;
++    prep*:SunOS:5.*:*)
++	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
++	exit ;;
++    *:GNU:*:*)
++	# the GNU system
++	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
++	exit ;;
++    *:GNU/*:*:*)
++	# other systems with GNU libc and userland
++	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
++	exit ;;
++    i*86:Minix:*:*)
++	echo ${UNAME_MACHINE}-pc-minix
++	exit ;;
++    arm*:Linux:*:*)
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
++	exit ;;
++    cris:Linux:*:*)
++	echo cris-axis-linux-gnu
++	exit ;;
++    crisv32:Linux:*:*)
++	echo crisv32-axis-linux-gnu
++	exit ;;
++    frv:Linux:*:*)
++    	echo frv-unknown-linux-gnu
++	exit ;;
++    ia64:Linux:*:*)
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
++	exit ;;
++    m32r*:Linux:*:*)
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
++	exit ;;
++    m68*:Linux:*:*)
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
++	exit ;;
++    mips:Linux:*:*)
++	eval $set_cc_for_build
++	sed 's/^	//' << EOF >$dummy.c
++	#undef CPU
++	#undef mips
++	#undef mipsel
++	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
++	CPU=mipsel
++	#else
++	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
++	CPU=mips
++	#else
++	CPU=
++	#endif
++	#endif
++EOF
++	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
++	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
++	;;
++    mips64:Linux:*:*)
++	eval $set_cc_for_build
++	sed 's/^	//' << EOF >$dummy.c
++	#undef CPU
++	#undef mips64
++	#undef mips64el
++	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
++	CPU=mips64el
++	#else
++	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
++	CPU=mips64
++	#else
++	CPU=
++	#endif
++	#endif
++EOF
++	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
++	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
++	;;
++    ppc:Linux:*:*)
++	echo powerpc-unknown-linux-gnu
++	exit ;;
++    ppc64:Linux:*:*)
++	echo powerpc64-unknown-linux-gnu
++	exit ;;
++    alpha:Linux:*:*)
++	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
++	  EV5)   UNAME_MACHINE=alphaev5 ;;
++	  EV56)  UNAME_MACHINE=alphaev56 ;;
++	  PCA56) UNAME_MACHINE=alphapca56 ;;
++	  PCA57) UNAME_MACHINE=alphapca56 ;;
++	  EV6)   UNAME_MACHINE=alphaev6 ;;
++	  EV67)  UNAME_MACHINE=alphaev67 ;;
++	  EV68*) UNAME_MACHINE=alphaev68 ;;
++        esac
++	objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
++	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
++	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
++	exit ;;
++    parisc:Linux:*:* | hppa:Linux:*:*)
++	# Look for CPU level
++	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
++	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
++	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
++	  *)    echo hppa-unknown-linux-gnu ;;
++	esac
++	exit ;;
++    parisc64:Linux:*:* | hppa64:Linux:*:*)
++	echo hppa64-unknown-linux-gnu
++	exit ;;
++    s390:Linux:*:* | s390x:Linux:*:*)
++	echo ${UNAME_MACHINE}-ibm-linux
++	exit ;;
++    sh64*:Linux:*:*)
++    	echo ${UNAME_MACHINE}-unknown-linux-gnu
++	exit ;;
++    sh*:Linux:*:*)
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
++	exit ;;
++    sparc:Linux:*:* | sparc64:Linux:*:*)
++	echo ${UNAME_MACHINE}-unknown-linux-gnu
++	exit ;;
++    x86_64:Linux:*:*)
++	echo x86_64-unknown-linux-gnu
++	exit ;;
++    i*86:Linux:*:*)
++	# The BFD linker knows what the default object file format is, so
++	# first see if it will tell us. cd to the root directory to prevent
++	# problems with other programs or directories called `ld' in the path.
++	# Set LC_ALL=C to ensure ld outputs messages in English.
++	ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
++			 | sed -ne '/supported targets:/!d
++				    s/[ 	][ 	]*/ /g
++				    s/.*supported targets: *//
++				    s/ .*//
++				    p'`
++        case "$ld_supported_targets" in
++	  elf32-i386)
++		TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
++		;;
++	  a.out-i386-linux)
++		echo "${UNAME_MACHINE}-pc-linux-gnuaout"
++		exit ;;
++	  coff-i386)
++		echo "${UNAME_MACHINE}-pc-linux-gnucoff"
++		exit ;;
++	  "")
++		# Either a pre-BFD a.out linker (linux-gnuoldld) or
++		# one that does not give us useful --help.
++		echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
++		exit ;;
++	esac
++	# Determine whether the default compiler is a.out or elf
++	eval $set_cc_for_build
++	sed 's/^	//' << EOF >$dummy.c
++	#include <features.h>
++	#ifdef __ELF__
++	# ifdef __GLIBC__
++	#  if __GLIBC__ >= 2
++	LIBC=gnu
++	#  else
++	LIBC=gnulibc1
++	#  endif
++	# else
++	LIBC=gnulibc1
++	# endif
++	#else
++	#ifdef __INTEL_COMPILER
++	LIBC=gnu
++	#else
++	LIBC=gnuaout
++	#endif
++	#endif
++	#ifdef __dietlibc__
++	LIBC=dietlibc
++	#endif
++EOF
++	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
++	test x"${LIBC}" != x && {
++		echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
++		exit
++	}
++	test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
++	;;
++    i*86:DYNIX/ptx:4*:*)
++	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
++	# earlier versions are messed up and put the nodename in both
++	# sysname and nodename.
++	echo i386-sequent-sysv4
++	exit ;;
++    i*86:UNIX_SV:4.2MP:2.*)
++        # Unixware is an offshoot of SVR4, but it has its own version
++        # number series starting with 2...
++        # I am not positive that other SVR4 systems won't match this,
++	# I just have to hope.  -- rms.
++        # Use sysv4.2uw... so that sysv4* matches it.
++	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
++	exit ;;
++    i*86:OS/2:*:*)
++	# If we were able to find `uname', then EMX Unix compatibility
++	# is probably installed.
++	echo ${UNAME_MACHINE}-pc-os2-emx
++	exit ;;
++    i*86:XTS-300:*:STOP)
++	echo ${UNAME_MACHINE}-unknown-stop
++	exit ;;
++    i*86:atheos:*:*)
++	echo ${UNAME_MACHINE}-unknown-atheos
++	exit ;;
++    i*86:syllable:*:*)
++	echo ${UNAME_MACHINE}-pc-syllable
++	exit ;;
++    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
++	echo i386-unknown-lynxos${UNAME_RELEASE}
++	exit ;;
++    i*86:*DOS:*:*)
++	echo ${UNAME_MACHINE}-pc-msdosdjgpp
++	exit ;;
++    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
++	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
++	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
++		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
++	else
++		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
++	fi
++	exit ;;
++    i*86:*:5:[678]*)
++    	# UnixWare 7.x, OpenUNIX and OpenServer 6.
++	case `/bin/uname -X | grep "^Machine"` in
++	    *486*)	     UNAME_MACHINE=i486 ;;
++	    *Pentium)	     UNAME_MACHINE=i586 ;;
++	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
++	esac
++	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
++	exit ;;
++    i*86:*:3.2:*)
++	if test -f /usr/options/cb.name; then
++		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
++		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
++	elif /bin/uname -X 2>/dev/null >/dev/null ; then
++		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
++		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
++		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
++			&& UNAME_MACHINE=i586
++		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
++			&& UNAME_MACHINE=i686
++		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
++			&& UNAME_MACHINE=i686
++		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
++	else
++		echo ${UNAME_MACHINE}-pc-sysv32
++	fi
++	exit ;;
++    pc:*:*:*)
++	# Left here for compatibility:
++        # uname -m prints for DJGPP always 'pc', but it prints nothing about
++        # the processor, so we play safe by assuming i386.
++	echo i386-pc-msdosdjgpp
++        exit ;;
++    Intel:Mach:3*:*)
++	echo i386-pc-mach3
++	exit ;;
++    paragon:*:*:*)
++	echo i860-intel-osf1
++	exit ;;
++    i860:*:4.*:*) # i860-SVR4
++	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
++	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
++	else # Add other i860-SVR4 vendors below as they are discovered.
++	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
++	fi
++	exit ;;
++    mini*:CTIX:SYS*5:*)
++	# "miniframe"
++	echo m68010-convergent-sysv
++	exit ;;
++    mc68k:UNIX:SYSTEM5:3.51m)
++	echo m68k-convergent-sysv
++	exit ;;
++    M680?0:D-NIX:5.3:*)
++	echo m68k-diab-dnix
++	exit ;;
++    M68*:*:R3V[5678]*:*)
++	test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
++    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
++	OS_REL=''
++	test -r /etc/.relid \
++	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
++	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
++	  && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
++	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
++	  && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
++    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
++        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
++          && { echo i486-ncr-sysv4; exit; } ;;
++    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
++	echo m68k-unknown-lynxos${UNAME_RELEASE}
++	exit ;;
++    mc68030:UNIX_System_V:4.*:*)
++	echo m68k-atari-sysv4
++	exit ;;
++    TSUNAMI:LynxOS:2.*:*)
++	echo sparc-unknown-lynxos${UNAME_RELEASE}
++	exit ;;
++    rs6000:LynxOS:2.*:*)
++	echo rs6000-unknown-lynxos${UNAME_RELEASE}
++	exit ;;
++    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
++	echo powerpc-unknown-lynxos${UNAME_RELEASE}
++	exit ;;
++    SM[BE]S:UNIX_SV:*:*)
++	echo mips-dde-sysv${UNAME_RELEASE}
++	exit ;;
++    RM*:ReliantUNIX-*:*:*)
++	echo mips-sni-sysv4
++	exit ;;
++    RM*:SINIX-*:*:*)
++	echo mips-sni-sysv4
++	exit ;;
++    *:SINIX-*:*:*)
++	if uname -p 2>/dev/null >/dev/null ; then
++		UNAME_MACHINE=`(uname -p) 2>/dev/null`
++		echo ${UNAME_MACHINE}-sni-sysv4
++	else
++		echo ns32k-sni-sysv
++	fi
++	exit ;;
++    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
++                      # says <Richard.M.Bartel@ccMail.Census.GOV>
++        echo i586-unisys-sysv4
++        exit ;;
++    *:UNIX_System_V:4*:FTX*)
++	# From Gerald Hewes <hewes@openmarket.com>.
++	# How about differentiating between stratus architectures? -djm
++	echo hppa1.1-stratus-sysv4
++	exit ;;
++    *:*:*:FTX*)
++	# From seanf@swdc.stratus.com.
++	echo i860-stratus-sysv4
++	exit ;;
++    i*86:VOS:*:*)
++	# From Paul.Green@stratus.com.
++	echo ${UNAME_MACHINE}-stratus-vos
++	exit ;;
++    *:VOS:*:*)
++	# From Paul.Green@stratus.com.
++	echo hppa1.1-stratus-vos
++	exit ;;
++    mc68*:A/UX:*:*)
++	echo m68k-apple-aux${UNAME_RELEASE}
++	exit ;;
++    news*:NEWS-OS:6*:*)
++	echo mips-sony-newsos6
++	exit ;;
++    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
++	if [ -d /usr/nec ]; then
++	        echo mips-nec-sysv${UNAME_RELEASE}
++	else
++	        echo mips-unknown-sysv${UNAME_RELEASE}
++	fi
++        exit ;;
++    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
++	echo powerpc-be-beos
++	exit ;;
++    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
++	echo powerpc-apple-beos
++	exit ;;
++    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
++	echo i586-pc-beos
++	exit ;;
++    SX-4:SUPER-UX:*:*)
++	echo sx4-nec-superux${UNAME_RELEASE}
++	exit ;;
++    SX-5:SUPER-UX:*:*)
++	echo sx5-nec-superux${UNAME_RELEASE}
++	exit ;;
++    SX-6:SUPER-UX:*:*)
++	echo sx6-nec-superux${UNAME_RELEASE}
++	exit ;;
++    Power*:Rhapsody:*:*)
++	echo powerpc-apple-rhapsody${UNAME_RELEASE}
++	exit ;;
++    *:Rhapsody:*:*)
++	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
++	exit ;;
++    *:Darwin:*:*)
++	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
++	case $UNAME_PROCESSOR in
++	    *86) UNAME_PROCESSOR=i686 ;;
++	    unknown) UNAME_PROCESSOR=powerpc ;;
++	esac
++	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
++	exit ;;
++    *:procnto*:*:* | *:QNX:[0123456789]*:*)
++	UNAME_PROCESSOR=`uname -p`
++	if test "$UNAME_PROCESSOR" = "x86"; then
++		UNAME_PROCESSOR=i386
++		UNAME_MACHINE=pc
++	fi
++	echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
++	exit ;;
++    *:QNX:*:4*)
++	echo i386-pc-qnx
++	exit ;;
++    NSE-?:NONSTOP_KERNEL:*:*)
++	echo nse-tandem-nsk${UNAME_RELEASE}
++	exit ;;
++    NSR-?:NONSTOP_KERNEL:*:*)
++	echo nsr-tandem-nsk${UNAME_RELEASE}
++	exit ;;
++    *:NonStop-UX:*:*)
++	echo mips-compaq-nonstopux
++	exit ;;
++    BS2000:POSIX*:*:*)
++	echo bs2000-siemens-sysv
++	exit ;;
++    DS/*:UNIX_System_V:*:*)
++	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
++	exit ;;
++    *:Plan9:*:*)
++	# "uname -m" is not consistent, so use $cputype instead. 386
++	# is converted to i386 for consistency with other x86
++	# operating systems.
++	if test "$cputype" = "386"; then
++	    UNAME_MACHINE=i386
++	else
++	    UNAME_MACHINE="$cputype"
++	fi
++	echo ${UNAME_MACHINE}-unknown-plan9
++	exit ;;
++    *:TOPS-10:*:*)
++	echo pdp10-unknown-tops10
++	exit ;;
++    *:TENEX:*:*)
++	echo pdp10-unknown-tenex
++	exit ;;
++    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
++	echo pdp10-dec-tops20
++	exit ;;
++    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
++	echo pdp10-xkl-tops20
++	exit ;;
++    *:TOPS-20:*:*)
++	echo pdp10-unknown-tops20
++	exit ;;
++    *:ITS:*:*)
++	echo pdp10-unknown-its
++	exit ;;
++    SEI:*:*:SEIUX)
++        echo mips-sei-seiux${UNAME_RELEASE}
++	exit ;;
++    *:DragonFly:*:*)
++	echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
++	exit ;;
++    *:*VMS:*:*)
++    	UNAME_MACHINE=`(uname -p) 2>/dev/null`
++	case "${UNAME_MACHINE}" in
++	    A*) echo alpha-dec-vms ; exit ;;
++	    I*) echo ia64-dec-vms ; exit ;;
++	    V*) echo vax-dec-vms ; exit ;;
++	esac ;;
++    *:XENIX:*:SysV)
++	echo i386-pc-xenix
++	exit ;;
++    i*86:skyos:*:*)
++	echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
++	exit ;;
++esac
++
++#echo '(No uname command or uname output not recognized.)' 1>&2
++#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
++
++eval $set_cc_for_build
++cat >$dummy.c <<EOF
++#ifdef _SEQUENT_
++# include <sys/types.h>
++# include <sys/utsname.h>
++#endif
++main ()
++{
++#if defined (sony)
++#if defined (MIPSEB)
++  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
++     I don't know....  */
++  printf ("mips-sony-bsd\n"); exit (0);
++#else
++#include <sys/param.h>
++  printf ("m68k-sony-newsos%s\n",
++#ifdef NEWSOS4
++          "4"
++#else
++	  ""
++#endif
++         ); exit (0);
++#endif
++#endif
++
++#if defined (__arm) && defined (__acorn) && defined (__unix)
++  printf ("arm-acorn-riscix\n"); exit (0);
++#endif
++
++#if defined (hp300) && !defined (hpux)
++  printf ("m68k-hp-bsd\n"); exit (0);
++#endif
++
++#if defined (NeXT)
++#if !defined (__ARCHITECTURE__)
++#define __ARCHITECTURE__ "m68k"
++#endif
++  int version;
++  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
++  if (version < 4)
++    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
++  else
++    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
++  exit (0);
++#endif
++
++#if defined (MULTIMAX) || defined (n16)
++#if defined (UMAXV)
++  printf ("ns32k-encore-sysv\n"); exit (0);
++#else
++#if defined (CMU)
++  printf ("ns32k-encore-mach\n"); exit (0);
++#else
++  printf ("ns32k-encore-bsd\n"); exit (0);
++#endif
++#endif
++#endif
++
++#if defined (__386BSD__)
++  printf ("i386-pc-bsd\n"); exit (0);
++#endif
++
++#if defined (sequent)
++#if defined (i386)
++  printf ("i386-sequent-dynix\n"); exit (0);
++#endif
++#if defined (ns32000)
++  printf ("ns32k-sequent-dynix\n"); exit (0);
++#endif
++#endif
++
++#if defined (_SEQUENT_)
++    struct utsname un;
++
++    uname(&un);
++
++    if (strncmp(un.version, "V2", 2) == 0) {
++	printf ("i386-sequent-ptx2\n"); exit (0);
++    }
++    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
++	printf ("i386-sequent-ptx1\n"); exit (0);
++    }
++    printf ("i386-sequent-ptx\n"); exit (0);
++
++#endif
++
++#if defined (vax)
++# if !defined (ultrix)
++#  include <sys/param.h>
++#  if defined (BSD)
++#   if BSD == 43
++      printf ("vax-dec-bsd4.3\n"); exit (0);
++#   else
++#    if BSD == 199006
++      printf ("vax-dec-bsd4.3reno\n"); exit (0);
++#    else
++      printf ("vax-dec-bsd\n"); exit (0);
++#    endif
++#   endif
++#  else
++    printf ("vax-dec-bsd\n"); exit (0);
++#  endif
++# else
++    printf ("vax-dec-ultrix\n"); exit (0);
++# endif
++#endif
++
++#if defined (alliant) && defined (i860)
++  printf ("i860-alliant-bsd\n"); exit (0);
++#endif
++
++  exit (1);
++}
++EOF
++
++$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
++	{ echo "$SYSTEM_NAME"; exit; }
++
++# Apollos put the system type in the environment.
++
++test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
++
++# Convex versions that predate uname can use getsysinfo(1)
++
++if [ -x /usr/convex/getsysinfo ]
++then
++    case `getsysinfo -f cpu_type` in
++    c1*)
++	echo c1-convex-bsd
++	exit ;;
++    c2*)
++	if getsysinfo -f scalar_acc
++	then echo c32-convex-bsd
++	else echo c2-convex-bsd
++	fi
++	exit ;;
++    c34*)
++	echo c34-convex-bsd
++	exit ;;
++    c38*)
++	echo c38-convex-bsd
++	exit ;;
++    c4*)
++	echo c4-convex-bsd
++	exit ;;
++    esac
++fi
++
++cat >&2 <<EOF
++$0: unable to guess system type
++
++This script, last modified $timestamp, has failed to recognize
++the operating system you are using. It is advised that you
++download the most up to date version of the config scripts from
++
++  http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
++and
++  http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
++
++If the version you run ($0) is already up to date, please
++send the following data and any information you think might be
++pertinent to <config-patches@gnu.org> in order to provide the needed
++information to handle your system.
++
++config.guess timestamp = $timestamp
++
++uname -m = `(uname -m) 2>/dev/null || echo unknown`
++uname -r = `(uname -r) 2>/dev/null || echo unknown`
++uname -s = `(uname -s) 2>/dev/null || echo unknown`
++uname -v = `(uname -v) 2>/dev/null || echo unknown`
++
++/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
++/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
++
++hostinfo               = `(hostinfo) 2>/dev/null`
++/bin/universe          = `(/bin/universe) 2>/dev/null`
++/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
++/bin/arch              = `(/bin/arch) 2>/dev/null`
++/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
++/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
++
++UNAME_MACHINE = ${UNAME_MACHINE}
++UNAME_RELEASE = ${UNAME_RELEASE}
++UNAME_SYSTEM  = ${UNAME_SYSTEM}
++UNAME_VERSION = ${UNAME_VERSION}
++EOF
++
++exit 1
++
++# Local variables:
++# eval: (add-hook 'write-file-hooks 'time-stamp)
++# time-stamp-start: "timestamp='"
++# time-stamp-format: "%:y-%02m-%02d"
++# time-stamp-end: "'"
++# End:
+diff --git a/utils/open-isns/aclocal/config.sub b/utils/open-isns/aclocal/config.sub
+new file mode 100644
+index 0000000..519f2cd
+--- /dev/null
++++ b/utils/open-isns/aclocal/config.sub
+@@ -0,0 +1,1570 @@
++#! /bin/sh
++# Configuration validation subroutine script.
++#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
++#   2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
++
++timestamp='2005-05-12'
++
++# This file is (in principle) common to ALL GNU software.
++# The presence of a machine in this file suggests that SOME GNU software
++# can handle that machine.  It does not imply ALL GNU software can.
++#
++# This file 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 2 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, write to the Free Software
++# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
++# 02110-1301, USA.
++#
++# As a special exception to the GNU General Public License, if you
++# distribute this file as part of a program that contains a
++# configuration script generated by Autoconf, you may include it under
++# the same distribution terms that you use for the rest of that program.
++
++
++# Please send patches to <config-patches@gnu.org>.  Submit a context
++# diff and a properly formatted ChangeLog entry.
++#
++# Configuration subroutine to validate and canonicalize a configuration type.
++# Supply the specified configuration type as an argument.
++# If it is invalid, we print an error message on stderr and exit with code 1.
++# Otherwise, we print the canonical config type on stdout and succeed.
++
++# This file is supposed to be the same for all GNU packages
++# and recognize all the CPU types, system types and aliases
++# that are meaningful with *any* GNU software.
++# Each package is responsible for reporting which valid configurations
++# it does not support.  The user should be able to distinguish
++# a failure to support a valid configuration from a meaningless
++# configuration.
++
++# The goal of this file is to map all the various variations of a given
++# machine specification into a single specification in the form:
++#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
++# or in some cases, the newer four-part form:
++#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
++# It is wrong to echo any other type of specification.
++
++me=`echo "$0" | sed -e 's,.*/,,'`
++
++usage="\
++Usage: $0 [OPTION] CPU-MFR-OPSYS
++       $0 [OPTION] ALIAS
++
++Canonicalize a configuration name.
++
++Operation modes:
++  -h, --help         print this help, then exit
++  -t, --time-stamp   print date of last modification, then exit
++  -v, --version      print version number, then exit
++
++Report bugs and patches to <config-patches@gnu.org>."
++
++version="\
++GNU config.sub ($timestamp)
++
++Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
++Free Software Foundation, Inc.
++
++This is free software; see the source for copying conditions.  There is NO
++warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
++
++help="
++Try \`$me --help' for more information."
++
++# Parse command line
++while test $# -gt 0 ; do
++  case $1 in
++    --time-stamp | --time* | -t )
++       echo "$timestamp" ; exit ;;
++    --version | -v )
++       echo "$version" ; exit ;;
++    --help | --h* | -h )
++       echo "$usage"; exit ;;
++    -- )     # Stop option processing
++       shift; break ;;
++    - )	# Use stdin as input.
++       break ;;
++    -* )
++       echo "$me: invalid option $1$help"
++       exit 1 ;;
++
++    *local*)
++       # First pass through any local machine types.
++       echo $1
++       exit ;;
++
++    * )
++       break ;;
++  esac
++done
++
++case $# in
++ 0) echo "$me: missing argument$help" >&2
++    exit 1;;
++ 1) ;;
++ *) echo "$me: too many arguments$help" >&2
++    exit 1;;
++esac
++
++# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
++# Here we must recognize all the valid KERNEL-OS combinations.
++maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
++case $maybe_os in
++  nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
++  kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
++    os=-$maybe_os
++    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
++    ;;
++  *)
++    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
++    if [ $basic_machine != $1 ]
++    then os=`echo $1 | sed 's/.*-/-/'`
++    else os=; fi
++    ;;
++esac
++
++### Let's recognize common machines as not being operating systems so
++### that things like config.sub decstation-3100 work.  We also
++### recognize some manufacturers as not being operating systems, so we
++### can provide default operating systems below.
++case $os in
++	-sun*os*)
++		# Prevent following clause from handling this invalid input.
++		;;
++	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
++	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
++	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
++	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
++	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
++	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
++	-apple | -axis | -knuth | -cray)
++		os=
++		basic_machine=$1
++		;;
++	-sim | -cisco | -oki | -wec | -winbond)
++		os=
++		basic_machine=$1
++		;;
++	-scout)
++		;;
++	-wrs)
++		os=-vxworks
++		basic_machine=$1
++		;;
++	-chorusos*)
++		os=-chorusos
++		basic_machine=$1
++		;;
++ 	-chorusrdb)
++ 		os=-chorusrdb
++		basic_machine=$1
++ 		;;
++	-hiux*)
++		os=-hiuxwe2
++		;;
++	-sco5)
++		os=-sco3.2v5
++		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
++		;;
++	-sco4)
++		os=-sco3.2v4
++		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
++		;;
++	-sco3.2.[4-9]*)
++		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
++		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
++		;;
++	-sco3.2v[4-9]*)
++		# Don't forget version if it is 3.2v4 or newer.
++		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
++		;;
++	-sco*)
++		os=-sco3.2v2
++		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
++		;;
++	-udk*)
++		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
++		;;
++	-isc)
++		os=-isc2.2
++		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
++		;;
++	-clix*)
++		basic_machine=clipper-intergraph
++		;;
++	-isc*)
++		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
++		;;
++	-lynx*)
++		os=-lynxos
++		;;
++	-ptx*)
++		basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
++		;;
++	-windowsnt*)
++		os=`echo $os | sed -e 's/windowsnt/winnt/'`
++		;;
++	-psos*)
++		os=-psos
++		;;
++	-mint | -mint[0-9]*)
++		basic_machine=m68k-atari
++		os=-mint
++		;;
++esac
++
++# Decode aliases for certain CPU-COMPANY combinations.
++case $basic_machine in
++	# Recognize the basic CPU types without company name.
++	# Some are omitted here because they have special meanings below.
++	1750a | 580 \
++	| a29k \
++	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
++	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
++	| am33_2.0 \
++	| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
++	| bfin \
++	| c4x | clipper \
++	| d10v | d30v | dlx | dsp16xx \
++	| fr30 | frv \
++	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
++	| i370 | i860 | i960 | ia64 \
++	| ip2k | iq2000 \
++	| m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \
++	| mips | mipsbe | mipseb | mipsel | mipsle \
++	| mips16 \
++	| mips64 | mips64el \
++	| mips64vr | mips64vrel \
++	| mips64orion | mips64orionel \
++	| mips64vr4100 | mips64vr4100el \
++	| mips64vr4300 | mips64vr4300el \
++	| mips64vr5000 | mips64vr5000el \
++	| mipsisa32 | mipsisa32el \
++	| mipsisa32r2 | mipsisa32r2el \
++	| mipsisa64 | mipsisa64el \
++	| mipsisa64r2 | mipsisa64r2el \
++	| mipsisa64sb1 | mipsisa64sb1el \
++	| mipsisa64sr71k | mipsisa64sr71kel \
++	| mipstx39 | mipstx39el \
++	| mn10200 | mn10300 \
++	| msp430 \
++	| ns16k | ns32k \
++	| openrisc | or32 \
++	| pdp10 | pdp11 | pj | pjl \
++	| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
++	| pyramid \
++	| sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
++	| sh64 | sh64le \
++	| sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \
++	| sparcv8 | sparcv9 | sparcv9b \
++	| strongarm \
++	| tahoe | thumb | tic4x | tic80 | tron \
++	| v850 | v850e \
++	| we32k \
++	| x86 | xscale | xscalee[bl] | xstormy16 | xtensa \
++	| z8k)
++		basic_machine=$basic_machine-unknown
++		;;
++	m6811 | m68hc11 | m6812 | m68hc12)
++		# Motorola 68HC11/12.
++		basic_machine=$basic_machine-unknown
++		os=-none
++		;;
++	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
++		;;
++
++	# We use `pc' rather than `unknown'
++	# because (1) that's what they normally are, and
++	# (2) the word "unknown" tends to confuse beginning users.
++	i*86 | x86_64)
++	  basic_machine=$basic_machine-pc
++	  ;;
++	# Object if more than one company name word.
++	*-*-*)
++		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
++		exit 1
++		;;
++	# Recognize the basic CPU types with company name.
++	580-* \
++	| a29k-* \
++	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
++	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
++	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
++	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
++	| avr-* \
++	| bfin-* | bs2000-* \
++	| c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
++	| clipper-* | craynv-* | cydra-* \
++	| d10v-* | d30v-* | dlx-* \
++	| elxsi-* \
++	| f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
++	| h8300-* | h8500-* \
++	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
++	| i*86-* | i860-* | i960-* | ia64-* \
++	| ip2k-* | iq2000-* \
++	| m32r-* | m32rle-* \
++	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
++	| m88110-* | m88k-* | maxq-* | mcore-* \
++	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
++	| mips16-* \
++	| mips64-* | mips64el-* \
++	| mips64vr-* | mips64vrel-* \
++	| mips64orion-* | mips64orionel-* \
++	| mips64vr4100-* | mips64vr4100el-* \
++	| mips64vr4300-* | mips64vr4300el-* \
++	| mips64vr5000-* | mips64vr5000el-* \
++	| mipsisa32-* | mipsisa32el-* \
++	| mipsisa32r2-* | mipsisa32r2el-* \
++	| mipsisa64-* | mipsisa64el-* \
++	| mipsisa64r2-* | mipsisa64r2el-* \
++	| mipsisa64sb1-* | mipsisa64sb1el-* \
++	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
++	| mipstx39-* | mipstx39el-* \
++	| mmix-* \
++	| msp430-* \
++	| none-* | np1-* | ns16k-* | ns32k-* \
++	| orion-* \
++	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
++	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
++	| pyramid-* \
++	| romp-* | rs6000-* \
++	| sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
++	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
++	| sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \
++	| sparclite-* \
++	| sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
++	| tahoe-* | thumb-* \
++	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
++	| tron-* \
++	| v850-* | v850e-* | vax-* \
++	| we32k-* \
++	| x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \
++	| xstormy16-* | xtensa-* \
++	| ymp-* \
++	| z8k-*)
++		;;
++	# Recognize the various machine names and aliases which stand
++	# for a CPU type and a company and sometimes even an OS.
++	386bsd)
++		basic_machine=i386-unknown
++		os=-bsd
++		;;
++	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
++		basic_machine=m68000-att
++		;;
++	3b*)
++		basic_machine=we32k-att
++		;;
++	a29khif)
++		basic_machine=a29k-amd
++		os=-udi
++		;;
++    	abacus)
++		basic_machine=abacus-unknown
++		;;
++	adobe68k)
++		basic_machine=m68010-adobe
++		os=-scout
++		;;
++	alliant | fx80)
++		basic_machine=fx80-alliant
++		;;
++	altos | altos3068)
++		basic_machine=m68k-altos
++		;;
++	am29k)
++		basic_machine=a29k-none
++		os=-bsd
++		;;
++	amd64)
++		basic_machine=x86_64-pc
++		;;
++	amd64-*)
++		basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
++		;;
++	amdahl)
++		basic_machine=580-amdahl
++		os=-sysv
++		;;
++	amiga | amiga-*)
++		basic_machine=m68k-unknown
++		;;
++	amigaos | amigados)
++		basic_machine=m68k-unknown
++		os=-amigaos
++		;;
++	amigaunix | amix)
++		basic_machine=m68k-unknown
++		os=-sysv4
++		;;
++	apollo68)
++		basic_machine=m68k-apollo
++		os=-sysv
++		;;
++	apollo68bsd)
++		basic_machine=m68k-apollo
++		os=-bsd
++		;;
++	aux)
++		basic_machine=m68k-apple
++		os=-aux
++		;;
++	balance)
++		basic_machine=ns32k-sequent
++		os=-dynix
++		;;
++	c90)
++		basic_machine=c90-cray
++		os=-unicos
++		;;
++	convex-c1)
++		basic_machine=c1-convex
++		os=-bsd
++		;;
++	convex-c2)
++		basic_machine=c2-convex
++		os=-bsd
++		;;
++	convex-c32)
++		basic_machine=c32-convex
++		os=-bsd
++		;;
++	convex-c34)
++		basic_machine=c34-convex
++		os=-bsd
++		;;
++	convex-c38)
++		basic_machine=c38-convex
++		os=-bsd
++		;;
++	cray | j90)
++		basic_machine=j90-cray
++		os=-unicos
++		;;
++	craynv)
++		basic_machine=craynv-cray
++		os=-unicosmp
++		;;
++	cr16c)
++		basic_machine=cr16c-unknown
++		os=-elf
++		;;
++	crds | unos)
++		basic_machine=m68k-crds
++		;;
++	crisv32 | crisv32-* | etraxfs*)
++		basic_machine=crisv32-axis
++		;;
++	cris | cris-* | etrax*)
++		basic_machine=cris-axis
++		;;
++	crx)
++		basic_machine=crx-unknown
++		os=-elf
++		;;
++	da30 | da30-*)
++		basic_machine=m68k-da30
++		;;
++	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
++		basic_machine=mips-dec
++		;;
++	decsystem10* | dec10*)
++		basic_machine=pdp10-dec
++		os=-tops10
++		;;
++	decsystem20* | dec20*)
++		basic_machine=pdp10-dec
++		os=-tops20
++		;;
++	delta | 3300 | motorola-3300 | motorola-delta \
++	      | 3300-motorola | delta-motorola)
++		basic_machine=m68k-motorola
++		;;
++	delta88)
++		basic_machine=m88k-motorola
++		os=-sysv3
++		;;
++	djgpp)
++		basic_machine=i586-pc
++		os=-msdosdjgpp
++		;;
++	dpx20 | dpx20-*)
++		basic_machine=rs6000-bull
++		os=-bosx
++		;;
++	dpx2* | dpx2*-bull)
++		basic_machine=m68k-bull
++		os=-sysv3
++		;;
++	ebmon29k)
++		basic_machine=a29k-amd
++		os=-ebmon
++		;;
++	elxsi)
++		basic_machine=elxsi-elxsi
++		os=-bsd
++		;;
++	encore | umax | mmax)
++		basic_machine=ns32k-encore
++		;;
++	es1800 | OSE68k | ose68k | ose | OSE)
++		basic_machine=m68k-ericsson
++		os=-ose
++		;;
++	fx2800)
++		basic_machine=i860-alliant
++		;;
++	genix)
++		basic_machine=ns32k-ns
++		;;
++	gmicro)
++		basic_machine=tron-gmicro
++		os=-sysv
++		;;
++	go32)
++		basic_machine=i386-pc
++		os=-go32
++		;;
++	h3050r* | hiux*)
++		basic_machine=hppa1.1-hitachi
++		os=-hiuxwe2
++		;;
++	h8300hms)
++		basic_machine=h8300-hitachi
++		os=-hms
++		;;
++	h8300xray)
++		basic_machine=h8300-hitachi
++		os=-xray
++		;;
++	h8500hms)
++		basic_machine=h8500-hitachi
++		os=-hms
++		;;
++	harris)
++		basic_machine=m88k-harris
++		os=-sysv3
++		;;
++	hp300-*)
++		basic_machine=m68k-hp
++		;;
++	hp300bsd)
++		basic_machine=m68k-hp
++		os=-bsd
++		;;
++	hp300hpux)
++		basic_machine=m68k-hp
++		os=-hpux
++		;;
++	hp3k9[0-9][0-9] | hp9[0-9][0-9])
++		basic_machine=hppa1.0-hp
++		;;
++	hp9k2[0-9][0-9] | hp9k31[0-9])
++		basic_machine=m68000-hp
++		;;
++	hp9k3[2-9][0-9])
++		basic_machine=m68k-hp
++		;;
++	hp9k6[0-9][0-9] | hp6[0-9][0-9])
++		basic_machine=hppa1.0-hp
++		;;
++	hp9k7[0-79][0-9] | hp7[0-79][0-9])
++		basic_machine=hppa1.1-hp
++		;;
++	hp9k78[0-9] | hp78[0-9])
++		# FIXME: really hppa2.0-hp
++		basic_machine=hppa1.1-hp
++		;;
++	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
++		# FIXME: really hppa2.0-hp
++		basic_machine=hppa1.1-hp
++		;;
++	hp9k8[0-9][13679] | hp8[0-9][13679])
++		basic_machine=hppa1.1-hp
++		;;
++	hp9k8[0-9][0-9] | hp8[0-9][0-9])
++		basic_machine=hppa1.0-hp
++		;;
++	hppa-next)
++		os=-nextstep3
++		;;
++	hppaosf)
++		basic_machine=hppa1.1-hp
++		os=-osf
++		;;
++	hppro)
++		basic_machine=hppa1.1-hp
++		os=-proelf
++		;;
++	i370-ibm* | ibm*)
++		basic_machine=i370-ibm
++		;;
++# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
++	i*86v32)
++		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
++		os=-sysv32
++		;;
++	i*86v4*)
++		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
++		os=-sysv4
++		;;
++	i*86v)
++		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
++		os=-sysv
++		;;
++	i*86sol2)
++		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
++		os=-solaris2
++		;;
++	i386mach)
++		basic_machine=i386-mach
++		os=-mach
++		;;
++	i386-vsta | vsta)
++		basic_machine=i386-unknown
++		os=-vsta
++		;;
++	iris | iris4d)
++		basic_machine=mips-sgi
++		case $os in
++		    -irix*)
++			;;
++		    *)
++			os=-irix4
++			;;
++		esac
++		;;
++	isi68 | isi)
++		basic_machine=m68k-isi
++		os=-sysv
++		;;
++	m88k-omron*)
++		basic_machine=m88k-omron
++		;;
++	magnum | m3230)
++		basic_machine=mips-mips
++		os=-sysv
++		;;
++	merlin)
++		basic_machine=ns32k-utek
++		os=-sysv
++		;;
++	mingw32)
++		basic_machine=i386-pc
++		os=-mingw32
++		;;
++	miniframe)
++		basic_machine=m68000-convergent
++		;;
++	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
++		basic_machine=m68k-atari
++		os=-mint
++		;;
++	mips3*-*)
++		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
++		;;
++	mips3*)
++		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
++		;;
++	monitor)
++		basic_machine=m68k-rom68k
++		os=-coff
++		;;
++	morphos)
++		basic_machine=powerpc-unknown
++		os=-morphos
++		;;
++	msdos)
++		basic_machine=i386-pc
++		os=-msdos
++		;;
++	mvs)
++		basic_machine=i370-ibm
++		os=-mvs
++		;;
++	ncr3000)
++		basic_machine=i486-ncr
++		os=-sysv4
++		;;
++	netbsd386)
++		basic_machine=i386-unknown
++		os=-netbsd
++		;;
++	netwinder)
++		basic_machine=armv4l-rebel
++		os=-linux
++		;;
++	news | news700 | news800 | news900)
++		basic_machine=m68k-sony
++		os=-newsos
++		;;
++	news1000)
++		basic_machine=m68030-sony
++		os=-newsos
++		;;
++	news-3600 | risc-news)
++		basic_machine=mips-sony
++		os=-newsos
++		;;
++	necv70)
++		basic_machine=v70-nec
++		os=-sysv
++		;;
++	next | m*-next )
++		basic_machine=m68k-next
++		case $os in
++		    -nextstep* )
++			;;
++		    -ns2*)
++		      os=-nextstep2
++			;;
++		    *)
++		      os=-nextstep3
++			;;
++		esac
++		;;
++	nh3000)
++		basic_machine=m68k-harris
++		os=-cxux
++		;;
++	nh[45]000)
++		basic_machine=m88k-harris
++		os=-cxux
++		;;
++	nindy960)
++		basic_machine=i960-intel
++		os=-nindy
++		;;
++	mon960)
++		basic_machine=i960-intel
++		os=-mon960
++		;;
++	nonstopux)
++		basic_machine=mips-compaq
++		os=-nonstopux
++		;;
++	np1)
++		basic_machine=np1-gould
++		;;
++	nsr-tandem)
++		basic_machine=nsr-tandem
++		;;
++	op50n-* | op60c-*)
++		basic_machine=hppa1.1-oki
++		os=-proelf
++		;;
++	or32 | or32-*)
++		basic_machine=or32-unknown
++		os=-coff
++		;;
++	os400)
++		basic_machine=powerpc-ibm
++		os=-os400
++		;;
++	OSE68000 | ose68000)
++		basic_machine=m68000-ericsson
++		os=-ose
++		;;
++	os68k)
++		basic_machine=m68k-none
++		os=-os68k
++		;;
++	pa-hitachi)
++		basic_machine=hppa1.1-hitachi
++		os=-hiuxwe2
++		;;
++	paragon)
++		basic_machine=i860-intel
++		os=-osf
++		;;
++	pbd)
++		basic_machine=sparc-tti
++		;;
++	pbb)
++		basic_machine=m68k-tti
++		;;
++	pc532 | pc532-*)
++		basic_machine=ns32k-pc532
++		;;
++	pentium | p5 | k5 | k6 | nexgen | viac3)
++		basic_machine=i586-pc
++		;;
++	pentiumpro | p6 | 6x86 | athlon | athlon_*)
++		basic_machine=i686-pc
++		;;
++	pentiumii | pentium2 | pentiumiii | pentium3)
++		basic_machine=i686-pc
++		;;
++	pentium4)
++		basic_machine=i786-pc
++		;;
++	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
++		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
++		;;
++	pentiumpro-* | p6-* | 6x86-* | athlon-*)
++		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
++		;;
++	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
++		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
++		;;
++	pentium4-*)
++		basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
++		;;
++	pn)
++		basic_machine=pn-gould
++		;;
++	power)	basic_machine=power-ibm
++		;;
++	ppc)	basic_machine=powerpc-unknown
++		;;
++	ppc-*)	basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
++		;;
++	ppcle | powerpclittle | ppc-le | powerpc-little)
++		basic_machine=powerpcle-unknown
++		;;
++	ppcle-* | powerpclittle-*)
++		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
++		;;
++	ppc64)	basic_machine=powerpc64-unknown
++		;;
++	ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
++		;;
++	ppc64le | powerpc64little | ppc64-le | powerpc64-little)
++		basic_machine=powerpc64le-unknown
++		;;
++	ppc64le-* | powerpc64little-*)
++		basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
++		;;
++	ps2)
++		basic_machine=i386-ibm
++		;;
++	pw32)
++		basic_machine=i586-unknown
++		os=-pw32
++		;;
++	rom68k)
++		basic_machine=m68k-rom68k
++		os=-coff
++		;;
++	rm[46]00)
++		basic_machine=mips-siemens
++		;;
++	rtpc | rtpc-*)
++		basic_machine=romp-ibm
++		;;
++	s390 | s390-*)
++		basic_machine=s390-ibm
++		;;
++	s390x | s390x-*)
++		basic_machine=s390x-ibm
++		;;
++	sa29200)
++		basic_machine=a29k-amd
++		os=-udi
++		;;
++	sb1)
++		basic_machine=mipsisa64sb1-unknown
++		;;
++	sb1el)
++		basic_machine=mipsisa64sb1el-unknown
++		;;
++	sei)
++		basic_machine=mips-sei
++		os=-seiux
++		;;
++	sequent)
++		basic_machine=i386-sequent
++		;;
++	sh)
++		basic_machine=sh-hitachi
++		os=-hms
++		;;
++	sh64)
++		basic_machine=sh64-unknown
++		;;
++	sparclite-wrs | simso-wrs)
++		basic_machine=sparclite-wrs
++		os=-vxworks
++		;;
++	sps7)
++		basic_machine=m68k-bull
++		os=-sysv2
++		;;
++	spur)
++		basic_machine=spur-unknown
++		;;
++	st2000)
++		basic_machine=m68k-tandem
++		;;
++	stratus)
++		basic_machine=i860-stratus
++		os=-sysv4
++		;;
++	sun2)
++		basic_machine=m68000-sun
++		;;
++	sun2os3)
++		basic_machine=m68000-sun
++		os=-sunos3
++		;;
++	sun2os4)
++		basic_machine=m68000-sun
++		os=-sunos4
++		;;
++	sun3os3)
++		basic_machine=m68k-sun
++		os=-sunos3
++		;;
++	sun3os4)
++		basic_machine=m68k-sun
++		os=-sunos4
++		;;
++	sun4os3)
++		basic_machine=sparc-sun
++		os=-sunos3
++		;;
++	sun4os4)
++		basic_machine=sparc-sun
++		os=-sunos4
++		;;
++	sun4sol2)
++		basic_machine=sparc-sun
++		os=-solaris2
++		;;
++	sun3 | sun3-*)
++		basic_machine=m68k-sun
++		;;
++	sun4)
++		basic_machine=sparc-sun
++		;;
++	sun386 | sun386i | roadrunner)
++		basic_machine=i386-sun
++		;;
++	sv1)
++		basic_machine=sv1-cray
++		os=-unicos
++		;;
++	symmetry)
++		basic_machine=i386-sequent
++		os=-dynix
++		;;
++	t3e)
++		basic_machine=alphaev5-cray
++		os=-unicos
++		;;
++	t90)
++		basic_machine=t90-cray
++		os=-unicos
++		;;
++	tic54x | c54x*)
++		basic_machine=tic54x-unknown
++		os=-coff
++		;;
++	tic55x | c55x*)
++		basic_machine=tic55x-unknown
++		os=-coff
++		;;
++	tic6x | c6x*)
++		basic_machine=tic6x-unknown
++		os=-coff
++		;;
++	tx39)
++		basic_machine=mipstx39-unknown
++		;;
++	tx39el)
++		basic_machine=mipstx39el-unknown
++		;;
++	toad1)
++		basic_machine=pdp10-xkl
++		os=-tops20
++		;;
++	tower | tower-32)
++		basic_machine=m68k-ncr
++		;;
++	tpf)
++		basic_machine=s390x-ibm
++		os=-tpf
++		;;
++	udi29k)
++		basic_machine=a29k-amd
++		os=-udi
++		;;
++	ultra3)
++		basic_machine=a29k-nyu
++		os=-sym1
++		;;
++	v810 | necv810)
++		basic_machine=v810-nec
++		os=-none
++		;;
++	vaxv)
++		basic_machine=vax-dec
++		os=-sysv
++		;;
++	vms)
++		basic_machine=vax-dec
++		os=-vms
++		;;
++	vpp*|vx|vx-*)
++		basic_machine=f301-fujitsu
++		;;
++	vxworks960)
++		basic_machine=i960-wrs
++		os=-vxworks
++		;;
++	vxworks68)
++		basic_machine=m68k-wrs
++		os=-vxworks
++		;;
++	vxworks29k)
++		basic_machine=a29k-wrs
++		os=-vxworks
++		;;
++	w65*)
++		basic_machine=w65-wdc
++		os=-none
++		;;
++	w89k-*)
++		basic_machine=hppa1.1-winbond
++		os=-proelf
++		;;
++	xbox)
++		basic_machine=i686-pc
++		os=-mingw32
++		;;
++	xps | xps100)
++		basic_machine=xps100-honeywell
++		;;
++	ymp)
++		basic_machine=ymp-cray
++		os=-unicos
++		;;
++	z8k-*-coff)
++		basic_machine=z8k-unknown
++		os=-sim
++		;;
++	none)
++		basic_machine=none-none
++		os=-none
++		;;
++
++# Here we handle the default manufacturer of certain CPU types.  It is in
++# some cases the only manufacturer, in others, it is the most popular.
++	w89k)
++		basic_machine=hppa1.1-winbond
++		;;
++	op50n)
++		basic_machine=hppa1.1-oki
++		;;
++	op60c)
++		basic_machine=hppa1.1-oki
++		;;
++	romp)
++		basic_machine=romp-ibm
++		;;
++	mmix)
++		basic_machine=mmix-knuth
++		;;
++	rs6000)
++		basic_machine=rs6000-ibm
++		;;
++	vax)
++		basic_machine=vax-dec
++		;;
++	pdp10)
++		# there are many clones, so DEC is not a safe bet
++		basic_machine=pdp10-unknown
++		;;
++	pdp11)
++		basic_machine=pdp11-dec
++		;;
++	we32k)
++		basic_machine=we32k-att
++		;;
++	sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
++		basic_machine=sh-unknown
++		;;
++	sh64)
++		basic_machine=sh64-unknown
++		;;
++	sparc | sparcv8 | sparcv9 | sparcv9b)
++		basic_machine=sparc-sun
++		;;
++	cydra)
++		basic_machine=cydra-cydrome
++		;;
++	orion)
++		basic_machine=orion-highlevel
++		;;
++	orion105)
++		basic_machine=clipper-highlevel
++		;;
++	mac | mpw | mac-mpw)
++		basic_machine=m68k-apple
++		;;
++	pmac | pmac-mpw)
++		basic_machine=powerpc-apple
++		;;
++	*-unknown)
++		# Make sure to match an already-canonicalized machine name.
++		;;
++	*)
++		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
++		exit 1
++		;;
++esac
++
++# Here we canonicalize certain aliases for manufacturers.
++case $basic_machine in
++	*-digital*)
++		basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
++		;;
++	*-commodore*)
++		basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
++		;;
++	*)
++		;;
++esac
++
++# Decode manufacturer-specific aliases for certain operating systems.
++
++if [ x"$os" != x"" ]
++then
++case $os in
++        # First match some system type aliases
++        # that might get confused with valid system types.
++	# -solaris* is a basic system type, with this one exception.
++	-solaris1 | -solaris1.*)
++		os=`echo $os | sed -e 's|solaris1|sunos4|'`
++		;;
++	-solaris)
++		os=-solaris2
++		;;
++	-svr4*)
++		os=-sysv4
++		;;
++	-unixware*)
++		os=-sysv4.2uw
++		;;
++	-gnu/linux*)
++		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
++		;;
++	# First accept the basic system types.
++	# The portable systems comes first.
++	# Each alternative MUST END IN A *, to match a version number.
++	# -sysv* is not here because it comes later, after sysvr4.
++	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
++	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
++	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
++	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
++	      | -aos* \
++	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
++	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
++	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
++	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
++	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
++	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
++	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
++	      | -chorusos* | -chorusrdb* \
++	      | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
++	      | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
++	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
++	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
++	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
++	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
++	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
++	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* | -skyos*)
++	# Remember, each alternative MUST END IN *, to match a version number.
++		;;
++	-qnx*)
++		case $basic_machine in
++		    x86-* | i*86-*)
++			;;
++		    *)
++			os=-nto$os
++			;;
++		esac
++		;;
++	-nto-qnx*)
++		;;
++	-nto*)
++		os=`echo $os | sed -e 's|nto|nto-qnx|'`
++		;;
++	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
++	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
++	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
++		;;
++	-mac*)
++		os=`echo $os | sed -e 's|mac|macos|'`
++		;;
++	-linux-dietlibc)
++		os=-linux-dietlibc
++		;;
++	-linux*)
++		os=`echo $os | sed -e 's|linux|linux-gnu|'`
++		;;
++	-sunos5*)
++		os=`echo $os | sed -e 's|sunos5|solaris2|'`
++		;;
++	-sunos6*)
++		os=`echo $os | sed -e 's|sunos6|solaris3|'`
++		;;
++	-opened*)
++		os=-openedition
++		;;
++        -os400*)
++		os=-os400
++		;;
++	-wince*)
++		os=-wince
++		;;
++	-osfrose*)
++		os=-osfrose
++		;;
++	-osf*)
++		os=-osf
++		;;
++	-utek*)
++		os=-bsd
++		;;
++	-dynix*)
++		os=-bsd
++		;;
++	-acis*)
++		os=-aos
++		;;
++	-atheos*)
++		os=-atheos
++		;;
++	-syllable*)
++		os=-syllable
++		;;
++	-386bsd)
++		os=-bsd
++		;;
++	-ctix* | -uts*)
++		os=-sysv
++		;;
++	-nova*)
++		os=-rtmk-nova
++		;;
++	-ns2 )
++		os=-nextstep2
++		;;
++	-nsk*)
++		os=-nsk
++		;;
++	# Preserve the version number of sinix5.
++	-sinix5.*)
++		os=`echo $os | sed -e 's|sinix|sysv|'`
++		;;
++	-sinix*)
++		os=-sysv4
++		;;
++        -tpf*)
++		os=-tpf
++		;;
++	-triton*)
++		os=-sysv3
++		;;
++	-oss*)
++		os=-sysv3
++		;;
++	-svr4)
++		os=-sysv4
++		;;
++	-svr3)
++		os=-sysv3
++		;;
++	-sysvr4)
++		os=-sysv4
++		;;
++	# This must come after -sysvr4.
++	-sysv*)
++		;;
++	-ose*)
++		os=-ose
++		;;
++	-es1800*)
++		os=-ose
++		;;
++	-xenix)
++		os=-xenix
++		;;
++	-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
++		os=-mint
++		;;
++	-aros*)
++		os=-aros
++		;;
++	-kaos*)
++		os=-kaos
++		;;
++	-zvmoe)
++		os=-zvmoe
++		;;
++	-none)
++		;;
++	*)
++		# Get rid of the `-' at the beginning of $os.
++		os=`echo $os | sed 's/[^-]*-//'`
++		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
++		exit 1
++		;;
++esac
++else
++
++# Here we handle the default operating systems that come with various machines.
++# The value should be what the vendor currently ships out the door with their
++# machine or put another way, the most popular os provided with the machine.
++
++# Note that if you're going to try to match "-MANUFACTURER" here (say,
++# "-sun"), then you have to tell the case statement up towards the top
++# that MANUFACTURER isn't an operating system.  Otherwise, code above
++# will signal an error saying that MANUFACTURER isn't an operating
++# system, and we'll never get to this point.
++
++case $basic_machine in
++	*-acorn)
++		os=-riscix1.2
++		;;
++	arm*-rebel)
++		os=-linux
++		;;
++	arm*-semi)
++		os=-aout
++		;;
++    c4x-* | tic4x-*)
++        os=-coff
++        ;;
++	# This must come before the *-dec entry.
++	pdp10-*)
++		os=-tops20
++		;;
++	pdp11-*)
++		os=-none
++		;;
++	*-dec | vax-*)
++		os=-ultrix4.2
++		;;
++	m68*-apollo)
++		os=-domain
++		;;
++	i386-sun)
++		os=-sunos4.0.2
++		;;
++	m68000-sun)
++		os=-sunos3
++		# This also exists in the configure program, but was not the
++		# default.
++		# os=-sunos4
++		;;
++	m68*-cisco)
++		os=-aout
++		;;
++	mips*-cisco)
++		os=-elf
++		;;
++	mips*-*)
++		os=-elf
++		;;
++	or32-*)
++		os=-coff
++		;;
++	*-tti)	# must be before sparc entry or we get the wrong os.
++		os=-sysv3
++		;;
++	sparc-* | *-sun)
++		os=-sunos4.1.1
++		;;
++	*-be)
++		os=-beos
++		;;
++	*-ibm)
++		os=-aix
++		;;
++    	*-knuth)
++		os=-mmixware
++		;;
++	*-wec)
++		os=-proelf
++		;;
++	*-winbond)
++		os=-proelf
++		;;
++	*-oki)
++		os=-proelf
++		;;
++	*-hp)
++		os=-hpux
++		;;
++	*-hitachi)
++		os=-hiux
++		;;
++	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
++		os=-sysv
++		;;
++	*-cbm)
++		os=-amigaos
++		;;
++	*-dg)
++		os=-dgux
++		;;
++	*-dolphin)
++		os=-sysv3
++		;;
++	m68k-ccur)
++		os=-rtu
++		;;
++	m88k-omron*)
++		os=-luna
++		;;
++	*-next )
++		os=-nextstep
++		;;
++	*-sequent)
++		os=-ptx
++		;;
++	*-crds)
++		os=-unos
++		;;
++	*-ns)
++		os=-genix
++		;;
++	i370-*)
++		os=-mvs
++		;;
++	*-next)
++		os=-nextstep3
++		;;
++	*-gould)
++		os=-sysv
++		;;
++	*-highlevel)
++		os=-bsd
++		;;
++	*-encore)
++		os=-bsd
++		;;
++	*-sgi)
++		os=-irix
++		;;
++	*-siemens)
++		os=-sysv4
++		;;
++	*-masscomp)
++		os=-rtu
++		;;
++	f30[01]-fujitsu | f700-fujitsu)
++		os=-uxpv
++		;;
++	*-rom68k)
++		os=-coff
++		;;
++	*-*bug)
++		os=-coff
++		;;
++	*-apple)
++		os=-macos
++		;;
++	*-atari*)
++		os=-mint
++		;;
++	*)
++		os=-none
++		;;
++esac
++fi
++
++# Here we handle the case where we know the os, and the CPU type, but not the
++# manufacturer.  We pick the logical manufacturer.
++vendor=unknown
++case $basic_machine in
++	*-unknown)
++		case $os in
++			-riscix*)
++				vendor=acorn
++				;;
++			-sunos*)
++				vendor=sun
++				;;
++			-aix*)
++				vendor=ibm
++				;;
++			-beos*)
++				vendor=be
++				;;
++			-hpux*)
++				vendor=hp
++				;;
++			-mpeix*)
++				vendor=hp
++				;;
++			-hiux*)
++				vendor=hitachi
++				;;
++			-unos*)
++				vendor=crds
++				;;
++			-dgux*)
++				vendor=dg
++				;;
++			-luna*)
++				vendor=omron
++				;;
++			-genix*)
++				vendor=ns
++				;;
++			-mvs* | -opened*)
++				vendor=ibm
++				;;
++			-os400*)
++				vendor=ibm
++				;;
++			-ptx*)
++				vendor=sequent
++				;;
++			-tpf*)
++				vendor=ibm
++				;;
++			-vxsim* | -vxworks* | -windiss*)
++				vendor=wrs
++				;;
++			-aux*)
++				vendor=apple
++				;;
++			-hms*)
++				vendor=hitachi
++				;;
++			-mpw* | -macos*)
++				vendor=apple
++				;;
++			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
++				vendor=atari
++				;;
++			-vos*)
++				vendor=stratus
++				;;
++		esac
++		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
++		;;
++esac
++
++echo $basic_machine$os
++exit
++
++# Local variables:
++# eval: (add-hook 'write-file-hooks 'time-stamp)
++# time-stamp-start: "timestamp='"
++# time-stamp-format: "%:y-%02m-%02d"
++# time-stamp-end: "'"
++# End:
+diff --git a/utils/open-isns/aclocal/install-sh b/utils/open-isns/aclocal/install-sh
+new file mode 100644
+index 0000000..220abbf
+--- /dev/null
++++ b/utils/open-isns/aclocal/install-sh
+@@ -0,0 +1,251 @@
++#!/bin/sh
++#
++# install - install a program, script, or datafile
++# This comes from X11R5 (mit/util/scripts/install.sh).
++#
++# Copyright 1991 by the Massachusetts Institute of Technology
++#
++# Permission to use, copy, modify, distribute, and sell this software and its
++# documentation for any purpose is hereby granted without fee, provided that
++# the above copyright notice appear in all copies and that both that
++# copyright notice and this permission notice appear in supporting
++# documentation, and that the name of M.I.T. not be used in advertising or
++# publicity pertaining to distribution of the software without specific,
++# written prior permission.  M.I.T. makes no representations about the
++# suitability of this software for any purpose.  It is provided "as is"
++# without express or implied warranty.
++#
++# Calling this script install-sh is preferred over install.sh, to prevent
++# `make' implicit rules from creating a file called install from it
++# when there is no Makefile.
++#
++# This script is compatible with the BSD install script, but was written
++# from scratch.  It can only install one file at a time, a restriction
++# shared with many OS's install programs.
++
++
++# set DOITPROG to echo to test this script
++
++# Don't use :- since 4.3BSD and earlier shells don't like it.
++doit="${DOITPROG-}"
++
++
++# put in absolute paths if you don't have them in your path; or use env. vars.
++
++mvprog="${MVPROG-mv}"
++cpprog="${CPPROG-cp}"
++chmodprog="${CHMODPROG-chmod}"
++chownprog="${CHOWNPROG-chown}"
++chgrpprog="${CHGRPPROG-chgrp}"
++stripprog="${STRIPPROG-strip}"
++rmprog="${RMPROG-rm}"
++mkdirprog="${MKDIRPROG-mkdir}"
++
++transformbasename=""
++transform_arg=""
++instcmd="$mvprog"
++chmodcmd="$chmodprog 0755"
++chowncmd=""
++chgrpcmd=""
++stripcmd=""
++rmcmd="$rmprog -f"
++mvcmd="$mvprog"
++src=""
++dst=""
++dir_arg=""
++
++while [ x"$1" != x ]; do
++    case $1 in
++	-c) instcmd="$cpprog"
++	    shift
++	    continue;;
++
++	-d) dir_arg=true
++	    shift
++	    continue;;
++
++	-m) chmodcmd="$chmodprog $2"
++	    shift
++	    shift
++	    continue;;
++
++	-o) chowncmd="$chownprog $2"
++	    shift
++	    shift
++	    continue;;
++
++	-g) chgrpcmd="$chgrpprog $2"
++	    shift
++	    shift
++	    continue;;
++
++	-s) stripcmd="$stripprog"
++	    shift
++	    continue;;
++
++	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
++	    shift
++	    continue;;
++
++	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
++	    shift
++	    continue;;
++
++	*)  if [ x"$src" = x ]
++	    then
++		src=$1
++	    else
++		# this colon is to work around a 386BSD /bin/sh bug
++		:
++		dst=$1
++	    fi
++	    shift
++	    continue;;
++    esac
++done
++
++if [ x"$src" = x ]
++then
++	echo "install:	no input file specified"
++	exit 1
++else
++	true
++fi
++
++if [ x"$dir_arg" != x ]; then
++	dst=$src
++	src=""
++	
++	if [ -d $dst ]; then
++		instcmd=:
++		chmodcmd=""
++	else
++		instcmd=mkdir
++	fi
++else
++
++# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
++# might cause directories to be created, which would be especially bad
++# if $src (and thus $dsttmp) contains '*'.
++
++	if [ -f $src -o -d $src ]
++	then
++		true
++	else
++		echo "install:  $src does not exist"
++		exit 1
++	fi
++	
++	if [ x"$dst" = x ]
++	then
++		echo "install:	no destination specified"
++		exit 1
++	else
++		true
++	fi
++
++# If destination is a directory, append the input filename; if your system
++# does not like double slashes in filenames, you may need to add some logic
++
++	if [ -d $dst ]
++	then
++		dst="$dst"/`basename $src`
++	else
++		true
++	fi
++fi
++
++## this sed command emulates the dirname command
++dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
++
++# Make sure that the destination directory exists.
++#  this part is taken from Noah Friedman's mkinstalldirs script
++
++# Skip lots of stat calls in the usual case.
++if [ ! -d "$dstdir" ]; then
++defaultIFS='	
++'
++IFS="${IFS-${defaultIFS}}"
++
++oIFS="${IFS}"
++# Some sh's can't handle IFS=/ for some reason.
++IFS='%'
++set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
++IFS="${oIFS}"
++
++pathcomp=''
++
++while [ $# -ne 0 ] ; do
++	pathcomp="${pathcomp}${1}"
++	shift
++
++	if [ ! -d "${pathcomp}" ] ;
++	then
++		$mkdirprog "${pathcomp}"
++	else
++		true
++	fi
++
++	pathcomp="${pathcomp}/"
++done
++fi
++
++if [ x"$dir_arg" != x ]
++then
++	$doit $instcmd $dst &&
++
++	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
++	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
++	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
++	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
++else
++
++# If we're going to rename the final executable, determine the name now.
++
++	if [ x"$transformarg" = x ]
++	then
++		dstfile=`basename $dst`
++	else
++		dstfile=`basename $dst $transformbasename |
++			sed $transformarg`$transformbasename
++	fi
++
++# don't allow the sed command to completely eliminate the filename
++
++	if [ x"$dstfile" = x ]
++	then
++		dstfile=`basename $dst`
++	else
++		true
++	fi
++
++# Make a temp file name in the proper directory.
++
++	dsttmp=$dstdir/#inst.$$#
++
++# Move or copy the file name to the temp name
++
++	$doit $instcmd $src $dsttmp &&
++
++	trap "rm -f ${dsttmp}" 0 &&
++
++# and set any options; do chmod last to preserve setuid bits
++
++# If any of these fail, we abort the whole thing.  If we want to
++# ignore errors from any of these, just make sure not to ignore
++# errors from the above "$doit $instcmd $src $dsttmp" command.
++
++	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
++	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
++	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
++	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
++
++# Now rename the file to the real destination.
++
++	$doit $rmcmd -f $dstdir/$dstfile &&
++	$doit $mvcmd $dsttmp $dstdir/$dstfile
++
++fi &&
++
++
++exit 0
+diff --git a/utils/open-isns/attrs.c b/utils/open-isns/attrs.c
+new file mode 100644
+index 0000000..12517c1
+--- /dev/null
++++ b/utils/open-isns/attrs.c
+@@ -0,0 +1,1618 @@
++/*
++ * Handle iSNS attributes and attribute lists
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <arpa/inet.h>
++#include "util.h"
++#include "vendor.h"
++#include "attrs.h"
++#include "isns.h"
++
++/* Implementation limit - sanity checking */
++#define ISNS_ATTR_MAX_LEN	8192
++
++static void	__isns_attr_set_value(isns_attr_t *, const isns_value_t *);
++
++/*
++ * Allocate an attribute
++ */
++isns_attr_t *
++isns_attr_alloc(uint32_t tag, const isns_tag_type_t *tag_type, const isns_value_t *value)
++{
++	isns_attr_t	*attr;
++
++	if (tag_type == NULL)
++		tag_type = isns_tag_type_by_id(tag);
++
++	attr = isns_calloc(1, sizeof(*attr));
++	if (!attr)
++		isns_fatal("Out of memory!\n");
++
++	attr->ia_users = 1;
++	attr->ia_tag_id = tag;
++	attr->ia_tag = tag_type;
++
++	__isns_attr_set_value(attr, value);
++	return attr;
++}
++
++isns_attr_t *
++isns_attr_get(isns_attr_t *attr)
++{
++	if (attr) {
++		isns_assert(attr->ia_users);
++		attr->ia_users++;
++	}
++	return attr;
++}
++
++void
++isns_attr_release(isns_attr_t *attr)
++{
++	const isns_attr_type_t *type;
++
++	isns_assert(attr->ia_users);
++	if (--(attr->ia_users))
++		return;
++
++	type = attr->ia_value.iv_type;
++	if (type->it_destroy)
++		type->it_destroy(&attr->ia_value);
++	isns_free(attr);
++}
++
++/*
++ * Assign a value to an attribute
++ */
++void
++__isns_attr_set_value(isns_attr_t *attr, const isns_value_t *new_value)
++{
++	const isns_attr_type_t *type, *old_type;
++	isns_value_t *old_value;
++
++	old_value = &attr->ia_value;
++	if (old_value == new_value)
++		return;
++
++	old_type = old_value->iv_type;
++	if (old_type && old_type->it_destroy)
++		old_type->it_destroy(old_value);
++
++	if (!new_value || !(type = new_value->iv_type))
++		type = attr->ia_tag->it_type;
++
++	/* When assigning the value to the attr, check
++	 * whether it needs special attention. */
++	if (new_value) {
++		if (type->it_assign) {
++			type->it_assign(&attr->ia_value, new_value);
++		} else {
++			attr->ia_value = *new_value;
++		}
++	}
++	attr->ia_value.iv_type = type;
++}
++
++/*
++ * Compare two attributes.
++ * Returns non-null when attributes are the same, else 0.
++ */
++int
++isns_attr_match(const isns_attr_t *a, const isns_attr_t *b)
++{
++	const isns_attr_type_t *type;
++
++	if (a->ia_tag_id != b->ia_tag_id)
++		return 0;
++
++	/* NIL acts as a wildcard */
++	if (a->ia_value.iv_type == &isns_attr_type_nil
++	 || b->ia_value.iv_type == &isns_attr_type_nil)
++		return 1;
++
++	if (a->ia_value.iv_type != b->ia_value.iv_type)
++		return 0;
++	type = a->ia_value.iv_type;
++
++	if (type->it_match)
++		return type->it_match(&a->ia_value, &b->ia_value);
++
++	return !memcmp(&a->ia_value, &b->ia_value, sizeof(isns_value_t));
++}
++
++/*
++ * Lexicographical comparison of two attributes.
++ * Returns -1 when a is less than b, +1 when a is greater than
++ * b, and 0 if equal.
++ */
++int
++isns_attr_compare(const isns_attr_t *a, const isns_attr_t *b)
++{
++	const isns_attr_type_t *type = a->ia_value.iv_type;
++
++	isns_assert(a->ia_tag_id == b->ia_tag_id);
++
++	if (type != b->ia_value.iv_type) {
++		/* One of them must be NIL */
++		if (type == &isns_attr_type_nil)
++			return -1;
++		return 1;
++	}
++
++	/* If both are NIL, consider them equal */
++	if (type == &isns_attr_type_nil)
++		return 0;
++
++	/* A few types need special comparison functions, but
++	 * most don't. The reason is, we don't care whether the
++	 * ordering this creates is the "canonical" ordering for
++	 * this type, eg for integers. All that matters is that
++	 * there is some consistent ordering suitable for
++	 * DevGetNext.
++	 */
++	if (type->it_compare)
++		return type->it_compare(&a->ia_value, &b->ia_value);
++
++	return memcmp(&a->ia_value, &b->ia_value, sizeof(isns_value_t));
++}
++
++/*
++ * Convert a string to an attribute
++ */
++isns_attr_t *
++isns_attr_from_string(uint32_t tag, const char *string)
++{
++	const isns_tag_type_t *tag_type;
++	int		(*parse)(isns_value_t *, const char *);
++	isns_value_t	value;
++
++	memset(&value, 0, sizeof(value));
++
++	tag_type = isns_tag_type_by_id(tag);
++	if (!tag_type)
++		return NULL;
++
++	parse = tag_type->it_parse;
++	if (parse == NULL)
++		parse = tag_type->it_type->it_parse;
++
++	if (!parse || !parse(&value, string))
++		return NULL;
++
++	return isns_attr_alloc(tag, tag_type, &value);
++}
++
++/*
++ * Initialize an attribute list.
++ */
++void
++isns_attr_list_init(isns_attr_list_t *list)
++{
++	memset(list, 0, sizeof(*list));
++}
++
++static inline void
++__isns_attr_list_resize(isns_attr_list_t *list, unsigned int count)
++{
++	unsigned int	max;
++
++	max = (list->ial_count + 15) & ~15;
++	if (count < max)
++		return;
++
++	count = (count + 15) & ~15;
++	list->ial_data = isns_realloc(list->ial_data, count * sizeof(isns_attr_t *));
++	if (!list->ial_data)
++		isns_fatal("Out of memory!\n");
++}
++
++void
++isns_attr_list_append_list(isns_attr_list_t *dst,
++		const isns_attr_list_t *src)
++{
++	unsigned int	i, j;
++
++	__isns_attr_list_resize(dst, dst->ial_count + src->ial_count);
++	j = dst->ial_count;
++	for (i = 0; i < src->ial_count; ++i, ++j) {
++		isns_attr_t *attr = src->ial_data[i];
++
++		dst->ial_data[j] = attr;
++		attr->ia_users++;
++	}
++	dst->ial_count = j;
++}
++
++void
++isns_attr_list_copy(isns_attr_list_t *dst,
++		const isns_attr_list_t *src)
++{
++	isns_attr_list_destroy(dst);
++	isns_attr_list_append_list(dst, src);
++}
++
++void
++isns_attr_list_destroy(isns_attr_list_t *list)
++{
++	unsigned int	i;
++
++	for (i = 0; i < list->ial_count; ++i) {
++		isns_attr_t *attr = list->ial_data[i];
++
++		isns_attr_release(attr);
++	}
++
++	if (list->ial_data)
++		isns_free(list->ial_data);
++	memset(list, 0, sizeof(*list));
++}
++
++int
++isns_attr_list_remove_tag(isns_attr_list_t *list, uint32_t tag)
++{
++	unsigned int	i = 0, j = 0, removed = 0;
++
++	for (i = 0; i < list->ial_count; ++i) {
++		isns_attr_t *attr = list->ial_data[i];
++
++		if (attr->ia_tag_id == tag) {
++			isns_attr_release(attr);
++			removed++;
++		} else {
++			list->ial_data[j++] = attr;
++		}
++	}
++	list->ial_count = j;
++	return removed;
++}
++
++/*
++ * Locate the given attribute in the list, remove it
++ * and any following attributes that have a tag from the
++ * @subordinate_tags list. This is used by the DDDereg
++ * code to remove DD members.
++ */
++int
++isns_attr_list_remove_member(isns_attr_list_t *list,
++		const isns_attr_t *match,
++		const uint32_t *subordinate_tags)
++{
++	unsigned int	i = 0, j = 0, k, removed = 0, purging = 0;
++
++	while (i < list->ial_count) {
++		isns_attr_t *attr = list->ial_data[i++];
++
++		if (purging && subordinate_tags) {
++			for (k = 0; subordinate_tags[k]; ++k) {
++				if (attr->ia_tag_id == subordinate_tags[k])
++					goto purge_attr;
++			}
++		}
++		purging = 0;
++
++		if (!isns_attr_match(attr, match)) {
++			list->ial_data[j++] = attr;
++			continue;
++		}
++
++purge_attr:
++		isns_attr_release(attr);
++		purging = 1;
++		removed++;
++	}
++	list->ial_count = j;
++	return removed;
++}
++
++/*
++ * Find the first attribute with the given tag
++ */
++static inline isns_attr_t *
++__isns_attr_list_find(const isns_attr_list_t *list, uint32_t tag)
++{
++	isns_attr_t	*attr;
++	unsigned int	i;
++
++	for (i = 0; i < list->ial_count; ++i) {
++		attr = list->ial_data[i];
++
++		if (attr->ia_tag_id == tag)
++			return attr;
++	}
++
++	return NULL;
++}
++
++/*
++ * Add a new attribute at the end of the list
++ */
++static inline void
++__isns_attr_list_append_attr(isns_attr_list_t *list, isns_attr_t *attr)
++{
++	__isns_attr_list_resize(list, list->ial_count + 1);
++	list->ial_data[list->ial_count++] = attr;
++}
++
++void
++isns_attr_list_append_attr(isns_attr_list_t *list, isns_attr_t *attr)
++{
++	attr->ia_users++;
++	__isns_attr_list_append_attr(list, attr);
++}
++
++/*
++ * Append an element to an attribute list
++ */
++static void
++__isns_attr_list_append(isns_attr_list_t *list,
++		uint32_t tag, const isns_tag_type_t *tag_type,
++		const isns_value_t *value)
++{
++	isns_attr_t	 *attr;
++
++	if (tag_type == NULL)
++		tag_type = isns_tag_type_by_id(tag);
++	if (value->iv_type != &isns_attr_type_nil
++	 && value->iv_type != tag_type->it_type) {
++		isns_warning("Using wrong type (%s) "
++			"when encoding attribute %04x (%s) - should be %s\n",
++			value->iv_type->it_name,
++			tag, tag_type->it_name,
++			tag_type->it_type->it_name);
++	}
++
++	attr = isns_attr_alloc(tag, tag_type, value);
++	__isns_attr_list_append_attr(list, attr);
++}
++
++/*
++ * Update an element to an attribute list
++ */
++static void
++__isns_attr_list_update(isns_attr_list_t *list,
++		uint32_t tag, const isns_tag_type_t *tag_type,
++		const isns_value_t *value)
++{
++	const isns_attr_type_t *type = value->iv_type;
++	isns_attr_t	 *attr;
++
++	if (tag_type == NULL)
++		tag_type = isns_tag_type_by_id(tag);
++	if (type != &isns_attr_type_nil
++	 && type != tag_type->it_type) {
++		isns_warning("Using wrong type (%s) "
++			"when encoding attribute %04x (%s) - should be %s\n",
++			type->it_name,
++			tag, tag_type->it_name,
++			tag_type->it_type->it_name);
++	}
++
++	if (tag_type->it_multiple
++	 || (attr = __isns_attr_list_find(list, tag)) == NULL) {
++		attr = isns_attr_alloc(tag, tag_type, NULL);
++		__isns_attr_list_append_attr(list, attr);
++	}
++
++	__isns_attr_set_value(attr, value);
++}
++
++/*
++ * Append an element to an attribute list - public interface
++ */
++void
++isns_attr_list_append_value(isns_attr_list_t *list,
++		uint32_t tag, const isns_tag_type_t *tag_type,
++		const isns_value_t *value)
++{
++	__isns_attr_list_append(list, tag, tag_type, value);
++}
++
++/*
++ * Update an element of an attribute list - public interface
++ */
++void
++isns_attr_list_update_value(isns_attr_list_t *list,
++		uint32_t tag, const isns_tag_type_t *tag_type,
++		const isns_value_t *value)
++{
++	__isns_attr_list_update(list, tag, tag_type, value);
++}
++
++void
++isns_attr_list_update_attr(isns_attr_list_t *list,
++		const isns_attr_t *attr)
++{
++	__isns_attr_list_update(list, attr->ia_tag_id,
++			attr->ia_tag, &attr->ia_value);
++}
++
++/*
++ * Replace an attribute on a list
++ */
++int
++isns_attr_list_replace_attr(isns_attr_list_t *list,
++		isns_attr_t *attr)
++{
++	unsigned int	i;
++
++	for (i = 0; i < list->ial_count; ++i) {
++		isns_attr_t	*other = list->ial_data[i];
++
++		if (other->ia_tag_id == attr->ia_tag_id) {
++			list->ial_data[i] = attr;
++			attr->ia_users++;
++			isns_attr_release(other);
++			return 1;
++		}
++	}
++	return 0;
++}
++
++/*
++ * Retrieve an element of an attribute list
++ */
++int
++isns_attr_list_get_attr(const isns_attr_list_t *list,
++		uint32_t tag, isns_attr_t **result)
++{
++	*result = __isns_attr_list_find(list, tag);
++	return *result != NULL;
++}
++
++int
++isns_attr_list_get_value(const isns_attr_list_t *list,
++		uint32_t tag, isns_value_t *value)
++{
++	isns_attr_t	*attr;
++
++	if (!(attr = __isns_attr_list_find(list, tag)))
++		return 0;
++
++	*value = attr->ia_value;
++	return 1;
++}
++
++int
++isns_attr_list_get_uint32(const isns_attr_list_t *list,
++		uint32_t tag, uint32_t *value)
++{
++	isns_attr_t	*attr;
++
++	if (!(attr = __isns_attr_list_find(list, tag))
++	 || !ISNS_ATTR_IS_UINT32(attr))
++		return 0;
++
++	*value = attr->ia_value.iv_uint32;
++	return 1;
++}
++
++int
++isns_attr_list_get_ipaddr(const isns_attr_list_t *list,
++		uint32_t tag, struct in6_addr *value)
++{
++	isns_attr_t	*attr;
++
++	if (!(attr = __isns_attr_list_find(list, tag))
++	 || !ISNS_ATTR_IS_IPADDR(attr))
++		return 0;
++
++	*value = attr->ia_value.iv_ipaddr;
++	return 1;
++}
++
++int
++isns_attr_list_get_string(const isns_attr_list_t *list,
++		uint32_t tag, const char **value)
++{
++	isns_attr_t	*attr;
++
++	if (!(attr = __isns_attr_list_find(list, tag))
++	 || !ISNS_ATTR_IS_STRING(attr))
++		return 0;
++
++	*value = attr->ia_value.iv_string;
++	return 1;
++}
++
++int
++isns_attr_list_contains(const isns_attr_list_t *list,
++		uint32_t tag)
++{
++	return __isns_attr_list_find(list, tag) != NULL;
++}
++
++/*
++ * Some attribute types have an implied ordering,
++ * which is needed for GetNext. This is used to
++ * compare two lists.
++ */
++
++/*
++ * Typed versions of isns_attr_list_append
++ */
++void
++isns_attr_list_append_nil(isns_attr_list_t *list, uint32_t tag)
++{
++	isns_value_t var = ISNS_VALUE_INIT(nil, 0);
++
++	__isns_attr_list_append(list, tag, NULL, &var);
++}
++
++void
++isns_attr_list_append_string(isns_attr_list_t *list,
++			uint32_t tag, const char *value)
++{
++	isns_value_t var = ISNS_VALUE_INIT(string, (char *) value);
++
++	__isns_attr_list_append(list, tag, NULL, &var);
++}
++
++void
++isns_attr_list_append_uint32(isns_attr_list_t *list,
++			uint32_t tag, uint32_t value)
++{
++	isns_value_t var = ISNS_VALUE_INIT(uint32, value);
++
++	__isns_attr_list_append(list, tag, NULL, &var);
++}
++
++void
++isns_attr_list_append_int32(isns_attr_list_t *list,
++			uint32_t tag, int32_t value)
++{
++	isns_value_t var = ISNS_VALUE_INIT(int32, value);
++
++	__isns_attr_list_append(list, tag, NULL, &var);
++}
++
++void
++isns_attr_list_append_uint64(isns_attr_list_t *list,
++			uint32_t tag, int64_t value)
++{
++	isns_value_t var = ISNS_VALUE_INIT(uint64, value);
++
++	__isns_attr_list_append(list, tag, NULL, &var);
++}
++
++void
++isns_attr_list_append_ipaddr(isns_attr_list_t *list,
++			uint32_t tag, const struct in6_addr *value)
++{
++	isns_value_t var = ISNS_VALUE_INIT(ipaddr, *value);
++
++	__isns_attr_list_append(list, tag, NULL, &var);
++}
++
++/*
++ * Untyped version of isns_attr_list_append and isns_attr_list_update.
++ * The caller must make sure that the type of @data matches the tag's type.
++ */
++int
++isns_attr_list_append(isns_attr_list_t *list, uint32_t tag, const void *data)
++{
++	const isns_tag_type_t *tag_type;
++	isns_value_t var;
++
++	if (!(tag_type = isns_tag_type_by_id(tag)))
++		return 0;
++
++	var.iv_type = tag_type->it_type;
++	if (!var.iv_type->it_set(&var, data))
++		return 0;
++
++	__isns_attr_list_append(list, tag, tag_type, &var);
++	return 1;
++}
++
++int
++isns_attr_list_update(isns_attr_list_t *list, uint32_t tag, const void *data)
++{
++	const isns_tag_type_t *tag_type;
++	isns_attr_type_t *type;
++	isns_value_t var;
++
++	if (!(tag_type = isns_tag_type_by_id(tag)))
++		return 0;
++
++	type = tag_type->it_type;
++	var.iv_type = type;
++	if (!type->it_set(&var, data))
++		return 0;
++
++	__isns_attr_list_update(list, tag, tag_type, &var);
++	return 1;
++}
++
++/*
++ * Validate the attribute list.
++ */
++int
++isns_attr_validate(const isns_attr_t *attr,
++		const isns_policy_t *policy)
++{
++	const isns_tag_type_t *tag_type;
++
++	tag_type = attr->ia_tag;
++	if (tag_type->it_validate == NULL)
++		return 1;
++	return tag_type->it_validate(&attr->ia_value, policy);
++}
++
++int
++isns_attr_list_validate(const isns_attr_list_t *list,
++			const isns_policy_t *policy,
++			unsigned int function)
++{
++	DECLARE_BITMAP(seen, __ISNS_TAG_MAX);
++	unsigned int	i;
++
++	for (i = 0; i < list->ial_count; ++i) {
++		const isns_tag_type_t *tag_type;
++		isns_attr_t	*attr = list->ial_data[i];
++		uint32_t	tag = attr->ia_tag_id;
++		unsigned int	bit;
++
++		if (attr == NULL)
++			return ISNS_INTERNAL_ERROR;
++
++		tag_type = attr->ia_tag;
++		if (tag_type == NULL)
++			return ISNS_INTERNAL_ERROR;
++
++		bit = tag;
++		if (OPENISNS_IS_PRIVATE_ATTR(tag))
++			bit -= OPENISNS_VENDOR_PREFIX;
++		if (bit >= __ISNS_TAG_MAX)
++			goto invalid;
++
++		if (attr->ia_value.iv_type == &isns_attr_type_nil) {
++			if (test_bit(seen, bit))
++				goto invalid;
++		} else
++		if (attr->ia_value.iv_type == tag_type->it_type) {
++			if (!tag_type->it_multiple && test_bit(seen, bit))
++				goto invalid;
++
++			if (!isns_attr_validate(attr, policy))
++				goto invalid;
++		} else {
++			return ISNS_INTERNAL_ERROR;
++		}
++
++		if (function == ISNS_DEVICE_ATTRIBUTE_REGISTER
++		 && tag_type->it_readonly)
++			goto invalid;
++
++		set_bit(seen, bit);
++	}
++
++	return ISNS_SUCCESS;
++
++invalid:
++	switch (function) {
++	case ISNS_DEVICE_ATTRIBUTE_REGISTER:
++		return ISNS_INVALID_REGISTRATION;
++
++	case ISNS_DEVICE_DEREGISTER:
++		return ISNS_INVALID_DEREGISTRATION;
++
++	case ISNS_DEVICE_ATTRIBUTE_QUERY:
++	case ISNS_DEVICE_GET_NEXT:
++		return ISNS_INVALID_QUERY;
++	}
++	return ISNS_ATTRIBUTE_NOT_IMPLEMENTED;
++}
++
++/*
++ * Debug helper: print attribute list
++ */
++void
++isns_attr_list_print(const isns_attr_list_t *list, isns_print_fn_t *fn)
++{
++	unsigned int	i;
++
++	for (i = 0; i < list->ial_count; ++i)
++		isns_attr_print(list->ial_data[i], fn);
++}
++
++char *
++isns_attr_print_value(const isns_attr_t *attr, char *buffer, size_t size)
++{
++	const isns_tag_type_t *tag_type = attr->ia_tag;
++	const isns_attr_type_t *type = attr->ia_value.iv_type;
++
++	if (tag_type->it_print && type == tag_type->it_type)
++		tag_type->it_print(&attr->ia_value, buffer, size);
++	else
++		type->it_print(&attr->ia_value, buffer, size);
++	return buffer;
++}
++
++void
++isns_attr_print(const isns_attr_t *attr, isns_print_fn_t *fn)
++{
++	const isns_tag_type_t *tag_type = attr->ia_tag;
++	const isns_attr_type_t *type = attr->ia_value.iv_type;
++	uint32_t	tag;
++	char		value[512], *vspec = "";
++
++	tag = attr->ia_tag_id;
++	if (OPENISNS_IS_PRIVATE_ATTR(tag)) {
++		tag -= OPENISNS_VENDOR_PREFIX;
++		vspec = "v";
++	}
++
++	fn("  %04x%1s %-12s: %s = %s\n",
++			tag, vspec,
++			type->it_name,
++			tag_type? tag_type->it_name : "Unknown Attribute",
++			isns_attr_print_value(attr, value, sizeof(value)));
++}
++
++/*
++ * TLV encode a single attribute
++ */
++int
++isns_attr_encode(buf_t *bp, const isns_attr_t *attr)
++{
++	const isns_value_t *value = &attr->ia_value;
++	const isns_attr_type_t *type = value->iv_type;
++
++	if (!buf_put32(bp, attr->ia_tag_id)
++	 || !type->it_encode(bp, value))
++		return ISNS_INTERNAL_ERROR;
++
++	return ISNS_SUCCESS;
++}
++
++/*
++ * TLV decode a single attribute
++ */
++int
++isns_attr_decode(buf_t *bp, isns_attr_t **result)
++{
++	isns_attr_t	*attr = NULL;
++	isns_value_t	*value;
++	uint32_t	tag, len;
++
++	if (!buf_get32(bp, &tag)
++	 || !buf_get32(bp, &len))
++		goto msg_fmt_error;
++
++	/* Attributes MUST be word aligned */
++	if (len & 3)
++		goto msg_fmt_error;
++
++	if (len > ISNS_ATTR_MAX_LEN)
++		goto msg_fmt_error;
++
++	/* Allocate the attribute */
++	attr = isns_attr_alloc(tag, NULL, NULL);
++
++	value = &attr->ia_value;
++	if (len == 0)
++		value->iv_type = &isns_attr_type_nil;
++
++	if (!value->iv_type->it_decode(bp, len, value))
++		goto msg_fmt_error;
++
++	*result = attr;
++	return ISNS_SUCCESS;
++
++msg_fmt_error:
++	isns_error("Error decoding attribute, tag=0x%04x, len=%u\n",
++				tag, len);
++	if (attr)
++		isns_attr_release(attr);
++	return ISNS_MESSAGE_FORMAT_ERROR;
++}
++
++
++/*
++ * Decode the list of TLV encoded attributes inside an
++ * iSNS message.
++ */
++static int
++__isns_attr_list_decode(buf_t *bp, isns_attr_list_t *list, int delimited)
++{
++	int	status;
++
++	while (buf_avail(bp)) {
++		isns_attr_t	*attr;
++
++		status = isns_attr_decode(bp, &attr);
++		if (status != ISNS_SUCCESS)
++			return status;
++
++		if (delimited && attr->ia_tag_id == ISNS_TAG_DELIMITER) {
++			isns_attr_release(attr);
++			break;
++		}
++
++		__isns_attr_list_append_attr(list, attr);
++	}
++
++	return ISNS_SUCCESS;
++}
++
++int
++isns_attr_list_decode(buf_t *bp, isns_attr_list_t *list)
++{
++	return __isns_attr_list_decode(bp, list, 0);
++}
++
++int
++isns_attr_list_decode_delimited(buf_t *bp, isns_attr_list_t *list)
++{
++	return __isns_attr_list_decode(bp, list, 1);
++}
++
++/*
++ * Remove all attributes from a list save those matching
++ * the given tags.
++ */
++void
++isns_attr_list_prune(isns_attr_list_t *list,
++		const uint32_t *tags, unsigned int num_tags)
++{
++	unsigned int	i, j, k;
++
++	for (i = j = 0; i < list->ial_count; ++i) {
++		isns_attr_t *attr = list->ial_data[i];
++
++		for (k = 0; k < num_tags; ++k) {
++			if (attr->ia_tag_id == tags[k]) {
++				list->ial_data[j++] = attr;
++				goto next;
++			}
++		}
++
++		isns_attr_release(attr);
++
++next:		;
++	}
++
++	list->ial_count = j;
++}
++
++/*
++ * TLV ecode the list of attributes to go with
++ * iSNS message.
++ */
++int
++isns_attr_list_encode(buf_t *bp, const isns_attr_list_t *list)
++{
++	unsigned int	i, status = ISNS_SUCCESS;
++
++	for (i = 0; i < list->ial_count; ++i) {
++		struct isns_attr *attr = list->ial_data[i];
++
++		status = isns_attr_encode(bp, attr);
++		if (status)
++			break;
++	}
++	return status;
++}
++
++/*
++ * Encode the delimiter attribute
++ */
++int
++isns_encode_delimiter(buf_t *bp)
++{
++	uint32_t tag = 0, len = 0;
++
++	if (!buf_put32(bp, tag)
++	 || !buf_put32(bp, len))
++		return ISNS_INTERNAL_ERROR;
++
++	return ISNS_SUCCESS;
++}
++
++/*
++ * Padded encoding
++ */
++static inline int
++isns_encode_padded(buf_t *bp, const void *ptr, size_t len)
++{
++	if (!buf_put(bp, ptr, len))
++		return 0;
++
++	if ((len & 3) == 0)
++		return 1;
++
++	return buf_put(bp, "\0\0\0", 4 - (len & 3));
++}
++
++/*
++ * Helper functions to deal with portal information
++ */
++void
++isns_portal_init(isns_portal_info_t *portal,
++		const struct sockaddr *saddr, int proto)
++{
++	const struct sockaddr_in *sin;
++
++	memset(portal, 0, sizeof(*portal));
++	switch (saddr->sa_family) {
++	case AF_INET6:
++		portal->addr = *(const struct sockaddr_in6 *) saddr;
++		break;
++
++	case AF_INET:
++		sin = (const struct sockaddr_in *) saddr;
++		portal->addr.sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
++		portal->addr.sin6_port = sin->sin_port;
++		portal->addr.sin6_family = AF_INET6;
++		break;
++	default:
++		isns_warning("Unknown address family in isns_portal_init\n");
++		return;
++	}
++
++	portal->proto = proto;
++}
++
++int
++isns_portal_from_attr_list(isns_portal_info_t *portal,
++				uint32_t addr_tag, uint32_t port_tag,
++				const isns_attr_list_t *list)
++{
++	const isns_attr_t *addr_attr = NULL, *port_attr = NULL;
++	unsigned int	i;
++
++	for (i = 0; i + 1 < list->ial_count; ++i) {
++		const isns_attr_t *attr = list->ial_data[i];
++
++		if (!ISNS_ATTR_IS_IPADDR(attr))
++			continue;
++		if (addr_tag && attr->ia_tag_id != addr_tag)
++			continue;
++		addr_attr = attr;
++		if (port_tag == 0) {
++			port_attr = list->ial_data[i + 1];
++			goto extract_portal;
++		}
++		break;
++	}
++
++	/* We have a specific port tag. */
++	while (++i < list->ial_count) {
++		const isns_attr_t *attr = list->ial_data[i];
++
++		if (attr->ia_tag_id == port_tag) {
++			port_attr = attr;
++			goto extract_portal;
++		}
++	}
++
++	return 0;
++
++extract_portal:
++	return isns_portal_from_attr_pair(portal,
++				addr_attr, port_attr);
++}
++
++int
++isns_portal_from_attr_pair(isns_portal_info_t *portal,
++				const isns_attr_t *addr_attr,
++				const isns_attr_t *port_attr)
++{
++	uint32_t	portspec;
++
++	memset(portal, 0, sizeof(*portal));
++	portal->addr.sin6_family = AF_INET6;
++
++	if (!ISNS_ATTR_IS_IPADDR(addr_attr)
++	 || !ISNS_ATTR_IS_UINT32(port_attr))
++		return 0;
++
++	portal->addr.sin6_addr = addr_attr->ia_value.iv_ipaddr;
++
++	portspec = port_attr->ia_value.iv_uint32;
++	portal->addr.sin6_port = htons(portspec & 0xffff);
++	portal->proto = (portspec & ISNS_PORTAL_PORT_UDP_MASK)? IPPROTO_UDP : IPPROTO_TCP;
++
++	return 1;
++}
++
++int
++isns_portal_to_attr_list(const isns_portal_info_t *portal,
++				uint32_t addr_tag, uint32_t port_tag,
++				isns_attr_list_t *list)
++{
++	uint32_t	portspec;
++
++	portspec = htons(portal->addr.sin6_port);
++	if (portal->proto == IPPROTO_UDP)
++		portspec |= ISNS_PORTAL_PORT_UDP_MASK;
++
++	{
++		isns_value_t addr_value = ISNS_VALUE_INIT(ipaddr, portal->addr.sin6_addr);
++		isns_value_t port_value = ISNS_VALUE_INIT(uint32, portspec);
++
++		isns_attr_list_update_value(list, addr_tag, NULL, &addr_value);
++		isns_attr_list_update_value(list, port_tag, NULL, &port_value);
++	}
++
++	return 1;
++}
++
++const char *
++isns_portal_string(const isns_portal_info_t *portal)
++{
++	const struct sockaddr_in6 *six = &portal->addr;
++	static char	buffer[128];
++	char		abuf[128];
++
++	inet_ntop(six->sin6_family, &six->sin6_addr, abuf, sizeof(abuf));
++	snprintf(buffer, sizeof(buffer), "[%s]:%d/%s",
++			abuf, ntohs(six->sin6_port),
++			(portal->proto == IPPROTO_UDP)? "udp" : "tcp");
++	return buffer;
++}
++
++int
++isns_portal_is_wildcard(const isns_portal_info_t *portal)
++{
++	return !memcmp(&portal->addr.sin6_addr,
++			&in6addr_any,
++			sizeof(struct in6_addr));
++}
++
++int
++isns_portal_equal(const isns_portal_info_t *a,
++		const isns_portal_info_t *b)
++{
++	if (a->proto != b->proto)
++		return 0;
++	return !memcmp(&a->addr, &b->addr, sizeof(a->addr));
++}
++
++uint32_t
++isns_portal_tcpudp_port(const isns_portal_info_t *portal)
++{
++	uint32_t	port;
++
++	port = isns_addr_get_port((const struct sockaddr *) &portal->addr);
++	if (portal->proto == IPPROTO_UDP)
++		port |= ISNS_PORTAL_PORT_UDP_MASK;
++	return port;
++}
++
++int
++isns_portal_parse(isns_portal_info_t *portal,
++			const char *spec,
++			const char *default_port)
++{
++	struct sockaddr_storage addr;
++	char	*copy, *psp;
++	int	alen, proto = IPPROTO_TCP, sock_type = SOCK_STREAM;
++
++	if (spec[0] == '/') {
++		isns_warning("%s: no AF_LOCAL addresses for portals!\n",
++				__FUNCTION__);
++		return 0;
++	}
++
++	/* Look at trailing /tcp or /udp */
++	copy = isns_strdup(spec);
++	if ((psp = strrchr(copy, '/')) != NULL) {
++		if (!strcasecmp(psp, "/udp")) {
++			sock_type = SOCK_DGRAM;
++			proto = IPPROTO_UDP;
++			*psp = '\0';
++		} else
++		if (!strcasecmp(psp, "/tcp")) {
++			sock_type = SOCK_STREAM;
++			proto = IPPROTO_TCP;
++			*psp = '\0';
++		}
++	}
++
++	alen = isns_get_address(&addr, copy, default_port, 0, sock_type, 0);
++	isns_free(copy);
++
++	if (alen < 0)
++		return 0;
++
++	isns_portal_init(portal, (struct sockaddr *) &addr, proto);
++	return 1;
++}
++
++/*
++ * Attribute type NIL
++ */
++static int
++isns_attr_type_nil_encode(buf_t *bp, const isns_value_t *value)
++{
++	return buf_put32(bp, 0);
++}
++
++static int
++isns_attr_type_nil_decode(buf_t *bp, size_t len, isns_value_t *value)
++{
++	return len == 0;
++}
++
++static void
++isns_attr_type_nil_print(const isns_value_t *value, char *buf, size_t size)
++{
++	snprintf(buf, size, "<empty>");
++}
++
++static int
++isns_attr_type_nil_parse(isns_value_t *value, const char *string)
++{
++	if (string && *string)
++		return 0;
++	return 1;
++}
++
++isns_attr_type_t isns_attr_type_nil = {
++	.it_id		= ISNS_ATTR_TYPE_NIL,
++	.it_name	= "nil",
++	.it_encode	= isns_attr_type_nil_encode,
++	.it_decode	= isns_attr_type_nil_decode,
++	.it_print	= isns_attr_type_nil_print,
++	.it_parse	= isns_attr_type_nil_parse,
++};
++
++/*
++ * Attribute type UINT32
++ */
++static int
++isns_attr_type_uint32_encode(buf_t *bp, const isns_value_t *value)
++{
++	return buf_put32(bp, 4) && buf_put32(bp, value->iv_uint32);
++}
++
++static int
++isns_attr_type_uint32_decode(buf_t *bp, size_t len, isns_value_t *value)
++{
++	if (len != 4)
++		return 0;
++	return buf_get32(bp, &value->iv_uint32);
++}
++
++static void
++isns_attr_type_uint32_print(const isns_value_t *value, char *buf, size_t size)
++{
++	snprintf(buf, size, "%u", value->iv_uint32);
++}
++
++static int
++isns_attr_type_uint32_parse(isns_value_t *value, const char *string)
++{
++	char	*end;
++
++	value->iv_uint32 = strtoul(string, &end, 0);
++	return *end == '\0';
++}
++
++static void
++isns_attr_type_int32_print(const isns_value_t *value, char *buf, size_t size)
++{
++	snprintf(buf, size, "%d", value->iv_uint32);
++}
++
++static int
++isns_attr_type_int32_parse(isns_value_t *value, const char *string)
++{
++	char	*end;
++
++	value->iv_int32 = strtol(string, &end, 0);
++	return *end == '\0';
++}
++
++isns_attr_type_t isns_attr_type_uint32 = {
++	.it_id		= ISNS_ATTR_TYPE_UINT32,
++	.it_name	= "uint32",
++	.it_encode	= isns_attr_type_uint32_encode,
++	.it_decode	= isns_attr_type_uint32_decode,
++	.it_print	= isns_attr_type_uint32_print,
++	.it_parse	= isns_attr_type_uint32_parse,
++};
++
++isns_attr_type_t isns_attr_type_int32 = {
++	.it_id		= ISNS_ATTR_TYPE_INT32,
++	.it_name	= "int32",
++	.it_encode	= isns_attr_type_uint32_encode,
++	.it_decode	= isns_attr_type_uint32_decode,
++	.it_print	= isns_attr_type_int32_print,
++	.it_parse	= isns_attr_type_int32_parse,
++};
++
++/*
++ * 16bit min/max
++ */
++static int
++isns_attr_type_range16_encode(buf_t *bp, const isns_value_t *value)
++{
++	uint32_t	word;
++
++	word = (value->iv_range.max << 16) | value->iv_range.min;
++	return buf_put32(bp, 4) && buf_put32(bp, word);
++}
++
++static int
++isns_attr_type_range16_decode(buf_t *bp, size_t len, isns_value_t *value)
++{
++	uint32_t	word;
++
++	if (len != 4)
++		return 0;
++	if (!buf_get32(bp, &word))
++		return 0;
++	value->iv_range.max = word >> 16;
++	value->iv_range.min = word & 0xFFFF;
++	return 1;
++}
++
++static void
++isns_attr_type_range16_print(const isns_value_t *value, char *buf, size_t size)
++{
++	snprintf(buf, size, "[%u, %u]", value->iv_range.min, value->iv_range.max);
++}
++
++isns_attr_type_t isns_attr_type_range16 = {
++	.it_id		= ISNS_ATTR_TYPE_RANGE16,
++	.it_name	= "range16",
++	.it_encode	= isns_attr_type_range16_encode,
++	.it_decode	= isns_attr_type_range16_decode,
++	.it_print	= isns_attr_type_range16_print,
++//	.it_parse	= isns_attr_type_range16_parse,
++};
++
++
++/*
++ * 64bit integers
++ */
++static int
++isns_attr_type_uint64_encode(buf_t *bp, const isns_value_t *value)
++{
++	return buf_put32(bp, 8) && buf_put64(bp, value->iv_uint64);
++}
++
++static int
++isns_attr_type_uint64_decode(buf_t *bp, size_t len, isns_value_t *value)
++{
++	if (len != 8)
++		return 0;
++	return buf_get64(bp, &value->iv_uint64);
++}
++
++static void
++isns_attr_type_uint64_print(const isns_value_t *value, char *buf, size_t size)
++{
++	snprintf(buf, size, "%Lu", (unsigned long long) value->iv_uint64);
++}
++
++static int
++isns_attr_type_uint64_parse(isns_value_t *value, const char *string)
++{
++	char	*end;
++
++	value->iv_uint64 = strtoull(string, &end, 0);
++	return *end == '\0';
++}
++
++isns_attr_type_t isns_attr_type_uint64 = {
++	.it_id		= ISNS_ATTR_TYPE_UINT64,
++	.it_name	= "uint64",
++	.it_encode	= isns_attr_type_uint64_encode,
++	.it_decode	= isns_attr_type_uint64_decode,
++	.it_print	= isns_attr_type_uint64_print,
++	.it_parse	= isns_attr_type_uint64_parse,
++};
++
++/*
++ * Attribute type STRING
++ */
++static void
++isns_attr_type_string_destroy(isns_value_t *value)
++{
++	isns_free(value->iv_string);
++	value->iv_string = NULL;
++}
++
++static int
++isns_attr_type_string_match(const isns_value_t *a, const isns_value_t *b)
++{
++	if (a->iv_string && b->iv_string)
++		return !strcmp(a->iv_string, b->iv_string);
++
++	return a->iv_string == b->iv_string;
++}
++
++static int
++isns_attr_type_string_compare(const isns_value_t *a, const isns_value_t *b)
++{
++	if (a->iv_string && b->iv_string)
++		return strcmp(a->iv_string, b->iv_string);
++
++	return a->iv_string? 1 : -1;
++}
++
++static int
++isns_attr_type_string_encode(buf_t *bp, const isns_value_t *value)
++{
++	uint32_t	len;
++
++	len = value->iv_string? strlen(value->iv_string) + 1 : 0;
++
++	if (!buf_put32(bp, ISNS_PAD(len)))
++		return 0;
++
++	if (len && !isns_encode_padded(bp, value->iv_string, len))
++		return 0;
++
++	return 1;
++}
++
++static int
++isns_attr_type_string_decode(buf_t *bp, size_t len, isns_value_t *value)
++{
++	/* Is this legal? */
++	if (len == 0)
++		return 1;
++
++	/* The string should be NUL terminated, but
++	 * better be safe than sorry. */
++	value->iv_string = isns_malloc(len + 1);
++	if (!buf_get(bp, value->iv_string, len)) {
++		isns_free(value->iv_string);
++		return 0;
++	}
++	value->iv_string[len] = '\0';
++	return 1;
++}
++
++static void
++isns_attr_type_string_print(const isns_value_t *value, char *buf, size_t size)
++{
++	if (!value->iv_string)
++		snprintf(buf, size, "(empty)");
++	else
++		snprintf(buf, size, "\"%s\"", value->iv_string);
++}
++
++static int
++isns_attr_type_string_parse(isns_value_t *value, const char *string)
++{
++	value->iv_string = isns_strdup(string);
++	return 1;
++}
++
++static void
++isns_attr_type_string_assign(isns_value_t *value, const isns_value_t *new_value)
++{
++	isns_assert(!value->iv_string);
++	if (new_value->iv_string)
++		value->iv_string = isns_strdup(new_value->iv_string);
++}
++
++isns_attr_type_t isns_attr_type_string = {
++	.it_id		= ISNS_ATTR_TYPE_STRING,
++	.it_name	= "string",
++	.it_assign	= isns_attr_type_string_assign,
++	.it_destroy	= isns_attr_type_string_destroy,
++	.it_match	= isns_attr_type_string_match,
++	.it_compare	= isns_attr_type_string_compare,
++	.it_encode	= isns_attr_type_string_encode,
++	.it_decode	= isns_attr_type_string_decode,
++	.it_print	= isns_attr_type_string_print,
++	.it_parse	= isns_attr_type_string_parse,
++};
++
++/*
++ * Attribute type IPADDR
++ */
++static int
++isns_attr_type_ipaddr_encode(buf_t *bp, const isns_value_t *value)
++{
++	if (!buf_put32(bp, 16)
++	 || !buf_put(bp, &value->iv_ipaddr, 16))
++		return 0;
++
++	return 1;
++}
++
++static int
++isns_attr_type_ipaddr_decode(buf_t *bp, size_t len, isns_value_t *value)
++{
++	if (len != 16)
++		return 0;
++
++	return buf_get(bp, &value->iv_ipaddr, 16);
++}
++
++static void
++isns_attr_type_ipaddr_print(const isns_value_t *value, char *buf, size_t size)
++{
++	const struct in6_addr *addr = &value->iv_ipaddr;
++	char	buffer[INET6_ADDRSTRLEN + 1];
++
++	/* The standard requires IPv4 mapping, but
++	 * some oldish implementations seem to use
++	 * IPv4 compatible addresss. */
++	if (IN6_IS_ADDR_V4MAPPED(addr) || IN6_IS_ADDR_V4COMPAT(addr)) {
++		struct in_addr ipv4;
++
++		ipv4.s_addr = addr->s6_addr32[3];
++		inet_ntop(AF_INET, &ipv4, buffer, sizeof(buffer));
++	} else {
++		inet_ntop(AF_INET6, addr, buffer, sizeof(buffer));
++	}
++	snprintf(buf, size, "%s", buffer);
++}
++
++static int
++isns_attr_type_ipaddr_parse(isns_value_t *value, const char *string)
++{
++	struct in_addr	addr4;
++
++	if (inet_pton(AF_INET, string, &addr4)) {
++		value->iv_ipaddr = in6addr_any;
++		value->iv_ipaddr.s6_addr32[3] = addr4.s_addr;
++		return 1;
++	}
++
++	return inet_pton(AF_INET6, string, &value->iv_ipaddr);
++}
++
++isns_attr_type_t isns_attr_type_ipaddr = {
++	.it_id		= ISNS_ATTR_TYPE_IPADDR,
++	.it_name	= "ipaddr",
++	.it_encode	= isns_attr_type_ipaddr_encode,
++	.it_decode	= isns_attr_type_ipaddr_decode,
++	.it_print	= isns_attr_type_ipaddr_print,
++	.it_parse	= isns_attr_type_ipaddr_parse,
++};
++
++/*
++ * Attribute type OPAQUE
++ */
++static void
++isns_attr_type_opaque_assign(isns_value_t *value, const isns_value_t *new_value)
++{
++	size_t new_len = new_value->iv_opaque.len;
++	isns_assert(value->iv_opaque.len == 0);
++	if (new_len) {
++		value->iv_opaque.ptr = isns_malloc(new_len);
++		value->iv_opaque.len = new_len;
++		memcpy(value->iv_opaque.ptr,
++				new_value->iv_opaque.ptr,
++				new_len);
++	}
++}
++
++static void
++isns_attr_type_opaque_destroy(isns_value_t *value)
++{
++	isns_free(value->iv_opaque.ptr);
++	value->iv_opaque.ptr = NULL;
++	value->iv_opaque.len = 0;
++}
++
++static int
++isns_attr_type_opaque_match(const isns_value_t *a, const isns_value_t *b)
++{
++	if (a->iv_opaque.len != b->iv_opaque.len)
++		return 0;
++	return !memcmp(a->iv_opaque.ptr, b->iv_opaque.ptr, a->iv_opaque.len);
++}
++
++static int
++isns_attr_type_opaque_compare(const isns_value_t *a, const isns_value_t *b)
++{
++	long	delta;
++
++	delta = a->iv_opaque.len - b->iv_opaque.len;
++	if (delta)
++		return delta;
++
++	return memcmp(a->iv_opaque.ptr, b->iv_opaque.ptr, a->iv_opaque.len);
++}
++
++static int
++isns_attr_type_opaque_encode(buf_t *bp, const isns_value_t *value)
++{
++	uint32_t	len;
++
++	len = value->iv_opaque.len;
++	if (len & 3)
++		return 0;
++
++	if (!buf_put32(bp, len)
++	 || !buf_put(bp, value->iv_opaque.ptr, len))
++		return 0;
++
++	return 1;
++}
++
++static int
++isns_attr_type_opaque_decode(buf_t *bp, size_t len, isns_value_t *value)
++{
++	value->iv_opaque.ptr = isns_malloc(len);
++	if (!buf_get(bp, value->iv_opaque.ptr, len)) {
++		isns_free(value->iv_opaque.ptr);
++		return 0;
++	}
++
++	value->iv_opaque.len = len;
++	return 1;
++}
++
++static void
++isns_attr_type_opaque_print(const isns_value_t *value, char *buf, size_t size)
++{
++	unsigned char	*data = value->iv_opaque.ptr;
++	unsigned int	i, len;
++
++	/* There must be room for "<...>\0" */
++	if (size < 6)
++		return;
++	size -= 6;
++
++	if ((len = value->iv_opaque.len) > 20)
++		len = 20;
++	if (size < 3 * len)
++		len = size / 3;
++
++	*buf++ = '<';
++	for (i = 0; i < len; ++i) {
++		if (i)
++			*buf++ = ' ';
++		sprintf(buf, "%02x", data[i]);
++		buf += 2;
++	}
++	if (len < value->iv_opaque.len) {
++		strcat(buf, "...");
++		buf += 4;
++	}
++	*buf++ = '>';
++	*buf++ = '\0';
++}
++
++isns_attr_type_t isns_attr_type_opaque = {
++	.it_id		= ISNS_ATTR_TYPE_OPAQUE,
++	.it_name	= "opaque",
++	.it_assign	= isns_attr_type_opaque_assign,
++	.it_destroy	= isns_attr_type_opaque_destroy,
++	.it_match	= isns_attr_type_opaque_match,
++	.it_compare	= isns_attr_type_opaque_compare,
++	.it_encode	= isns_attr_type_opaque_encode,
++	.it_decode	= isns_attr_type_opaque_decode,
++	.it_print	= isns_attr_type_opaque_print,
++};
++
++/*
++ * Map attribute type IDs to attribute types
++ */
++static isns_attr_type_t *
++isns_attr_types_builtin[__ISNS_ATTR_TYPE_BUILTIN_MAX] = {
++[ISNS_ATTR_TYPE_NIL]		= &isns_attr_type_nil,
++[ISNS_ATTR_TYPE_OPAQUE]		= &isns_attr_type_opaque,
++[ISNS_ATTR_TYPE_STRING]		= &isns_attr_type_string,
++[ISNS_ATTR_TYPE_INT32]		= &isns_attr_type_int32,
++[ISNS_ATTR_TYPE_UINT32]		= &isns_attr_type_uint32,
++[ISNS_ATTR_TYPE_UINT64]		= &isns_attr_type_uint64,
++[ISNS_ATTR_TYPE_IPADDR]		= &isns_attr_type_ipaddr,
++[ISNS_ATTR_TYPE_RANGE16]	= &isns_attr_type_range16,
++};
++
++const isns_attr_type_t *
++isns_attr_type_by_id(unsigned int id)
++{
++	if (id < __ISNS_ATTR_TYPE_BUILTIN_MAX)
++		return isns_attr_types_builtin[id];
++
++	/* TODO: handle dynamic registration of attrtypes
++	 * for vendor extensions. */
++	return NULL;
++}
+diff --git a/utils/open-isns/attrs.h b/utils/open-isns/attrs.h
+new file mode 100644
+index 0000000..1d3667e
+--- /dev/null
++++ b/utils/open-isns/attrs.h
+@@ -0,0 +1,262 @@
++/*
++ * iSNS object attributes
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_ATTRS_H
++#define ISNS_ATTRS_H
++
++#include <netinet/in.h>
++#include "buffer.h"
++#include "isns.h"
++
++/*
++ * Type identifier
++ */
++enum {
++	ISNS_ATTR_TYPE_NIL = 0,
++	ISNS_ATTR_TYPE_OPAQUE,
++	ISNS_ATTR_TYPE_STRING,
++	ISNS_ATTR_TYPE_INT32,
++	ISNS_ATTR_TYPE_UINT32,
++	ISNS_ATTR_TYPE_UINT64,
++	ISNS_ATTR_TYPE_IPADDR,
++	ISNS_ATTR_TYPE_RANGE16,
++
++	__ISNS_ATTR_TYPE_BUILTIN_MAX
++};
++
++/*
++ * Union holding an attribute value
++ */
++typedef struct isns_value {
++	const struct isns_attr_type *	iv_type;
++
++	/* Data is stuffed into an anonymous union */
++	union {
++		uint32_t		iv_nil;
++		struct __isns_opaque {
++			void *		ptr;
++			size_t		len;
++		}			iv_opaque;
++		char *			iv_string;
++		int32_t			iv_int32;
++		uint32_t		iv_uint32;
++		uint64_t		iv_uint64;
++		struct in6_addr		iv_ipaddr;
++		struct {
++			uint16_t	min, max;
++		}			iv_range;
++	};
++} isns_value_t;
++
++#define __ISNS_ATTRTYPE(type)	isns_attr_type_##type
++#define __ISNS_MEMBER(type)	iv_##type
++#define ISNS_VALUE_INIT(type, value) \
++	(isns_value_t) { .iv_type = &__ISNS_ATTRTYPE(type), \
++		         { .__ISNS_MEMBER(type) = (value) } }
++
++#define isns_attr_initialize(attrp, tag, type, value) do { \
++		isns_attr_t *__attr = (attrp);		\
++		uint32_t __tag = (tag);			\
++		__attr->ia_users = 1;			\
++		__attr->ia_tag_id = (__tag);		\
++		__attr->ia_tag = isns_tag_type_by_id(__tag); \
++		__attr->ia_value = ISNS_VALUE_INIT(type, value); \
++	} while (0)
++#define ISNS_ATTR_INIT(tag, type, value) (isns_attr_t) {	\
++	 	.ia_users = 1,					\
++		.ia_tag_id = (tag),				\
++		.ia_tag = isns_tag_type_by_id(tag),		\
++		.ia_value = ISNS_VALUE_INIT(type, value)	\
++	}
++
++/*
++ * Attribute type
++ */
++typedef struct isns_attr_type {
++	uint32_t	it_id;
++	const char *	it_name;
++
++	void		(*it_assign)(isns_value_t *, const isns_value_t *);
++	int		(*it_set)(isns_value_t *, const void *);
++	int		(*it_get)(isns_value_t *, void *);
++	int		(*it_match)(const isns_value_t *, const isns_value_t *);
++	int		(*it_compare)(const isns_value_t *, const isns_value_t *);
++	int		(*it_encode)(buf_t *, const isns_value_t *);
++	int		(*it_decode)(buf_t *, size_t, isns_value_t *);
++	void		(*it_destroy)(isns_value_t *);
++	void		(*it_print)(const isns_value_t *, char *, size_t);
++	int		(*it_parse)(isns_value_t *, const char *);
++} isns_attr_type_t;
++
++/*
++ * Tag info: for each tag, provides a printable name,
++ * and the attribute type associated with it.
++ */
++struct isns_tag_type {
++	uint32_t	it_id;
++	const char *	it_name;
++	unsigned int	it_multiple : 1,
++			it_readonly : 1;
++	isns_attr_type_t *it_type;
++
++	int		(*it_validate)(const isns_value_t *,
++					const isns_policy_t *);
++	void		(*it_print)(const isns_value_t *, char *, size_t);
++	int		(*it_parse)(isns_value_t *, const char *);
++	const char *	(*it_help)(void);
++};
++
++/*
++ * Attribute
++ */
++struct isns_attr {
++	unsigned int		ia_users;
++	uint32_t		ia_tag_id;
++	const isns_tag_type_t *	ia_tag;
++	isns_value_t		ia_value;
++};
++
++extern isns_attr_type_t	isns_attr_type_nil;
++extern isns_attr_type_t	isns_attr_type_opaque;
++extern isns_attr_type_t	isns_attr_type_string;
++extern isns_attr_type_t	isns_attr_type_int32;
++extern isns_attr_type_t	isns_attr_type_uint32;
++extern isns_attr_type_t	isns_attr_type_uint64;
++extern isns_attr_type_t	isns_attr_type_ipaddr;
++extern isns_attr_type_t	isns_attr_type_range16;
++
++extern isns_attr_t *	isns_attr_alloc(uint32_t, const isns_tag_type_t *,
++					const isns_value_t *);
++
++extern void		isns_attr_list_append_value(isns_attr_list_t *,
++					uint32_t tag, const isns_tag_type_t *,
++					const isns_value_t *);
++extern void		isns_attr_list_update_value(isns_attr_list_t *,
++					uint32_t tag, const isns_tag_type_t *,
++					const isns_value_t *);
++extern int		isns_attr_list_get_value(const isns_attr_list_t *,
++					uint32_t tag,
++					isns_value_t *);
++extern int		isns_attr_list_get_uint32(const isns_attr_list_t *,
++					uint32_t tag,
++					uint32_t *);
++extern int		isns_attr_list_get_string(const isns_attr_list_t *,
++					uint32_t tag,
++					const char **);
++
++extern int		isns_attr_list_validate(const isns_attr_list_t *,
++					const isns_policy_t *,
++					unsigned int function);
++extern int		isns_attr_validate(const isns_attr_t *,
++					const isns_policy_t *);
++
++extern void		isns_attr_list_prune(isns_attr_list_t *,
++					const uint32_t *,
++					unsigned int);
++extern int		isns_attr_list_remove_member(isns_attr_list_t *,
++					const isns_attr_t *,
++					const uint32_t *);
++extern void		isns_attr_list_update_attr(isns_attr_list_t *,
++					const isns_attr_t *);
++
++extern int		isns_attr_decode(buf_t *, isns_attr_t **);
++extern int		isns_attr_encode(buf_t *, const isns_attr_t *);
++
++extern int		isns_attr_list_decode(buf_t *, isns_attr_list_t *);
++extern int		isns_attr_list_decode_delimited(buf_t *, isns_attr_list_t *);
++extern int		isns_attr_list_encode(buf_t *, const isns_attr_list_t *);
++extern int		isns_encode_delimiter(buf_t *);
++
++extern const isns_tag_type_t *isns_tag_type_by_id(unsigned int);
++extern const isns_attr_type_t *isns_attr_type_by_id(unsigned int);
++
++typedef struct isns_quick_attr_list isns_quick_attr_list_t;
++struct isns_quick_attr_list {
++	isns_attr_list_t	iqa_list;
++	isns_attr_t *		iqa_attrs[1];
++	isns_attr_t		iqa_attr;
++};
++#define ISNS_QUICK_ATTR_LIST_DECLARE(qlist, tag, type, value) \
++	isns_quick_attr_list_t qlist = {			\
++		.iqa_list = (isns_attr_list_t) {		\
++			.ial_data = qlist.iqa_attrs,		\
++			.ial_count = 1				\
++		},						\
++		.iqa_attrs = { &qlist.iqa_attr },		\
++		.iqa_attr = ISNS_ATTR_INIT(tag, type, value),	\
++	}
++
++/*
++ * The following is used to chop up an incoming attr list as
++ * given in eg. a DevAttrReg message into separate chunks,
++ * following the ordering constraints laid out in the RFC.
++ *
++ * isns_attr_list_scanner_init initializes the scanner state.
++ *
++ * isns_attr_list_scanner_next advances to the next object in
++ * the list, returning the keys and attrs for one object.
++ *
++ * The isns_attr_list_scanner struct should really be opaque, but
++ * we put it here so you can declare a scanner variable on the
++ * stack.
++ */
++struct isns_attr_list_scanner {
++	isns_source_t *		source;
++	isns_policy_t *		policy;
++	isns_object_t *		key_obj;
++	isns_attr_list_t	orig_attrs;
++	unsigned int		pos;
++
++	isns_attr_list_t	keys;
++	isns_attr_list_t	attrs;
++	isns_object_template_t *tmpl;
++	unsigned int		num_key_attrs;
++
++	unsigned int		entities;
++
++	uint32_t		pgt_next_attr;
++	uint32_t		pgt_value;
++	const char *		pgt_iscsi_name;
++	isns_portal_info_t	pgt_portal_info;
++	isns_object_t *		pgt_base_object;
++
++	unsigned int		index_acceptable : 1;
++};
++
++extern void		isns_attr_list_scanner_init(struct isns_attr_list_scanner *,
++				isns_object_t *key_obj,
++				const isns_attr_list_t *attrs);
++extern int		isns_attr_list_scanner_next(struct isns_attr_list_scanner *);
++extern void		isns_attr_list_scanner_destroy(struct isns_attr_list_scanner *);
++
++/*
++ * The following is used to parse attribute lists given as
++ * a bunch of strings.
++ */
++struct isns_attr_list_parser {
++	struct isns_tag_prefix *prefix;
++	const char *		default_port;
++
++	unsigned int		multi_type_permitted : 1,
++				nil_permitted : 1;
++
++	isns_attr_t *		(*load_key)(const char *);
++	isns_attr_t *		(*generate_key)(void);
++};
++
++extern int		isns_attr_list_split(char *line, char **argv, unsigned int argc_max);
++extern void		isns_attr_list_parser_init(struct isns_attr_list_parser *,
++				isns_object_template_t *);
++extern int		isns_parse_attrs(unsigned int, char **,
++				isns_attr_list_t *, struct isns_attr_list_parser *);
++extern int		isns_parse_query_attrs(unsigned int, char **,
++				isns_attr_list_t *, isns_attr_list_t *,
++				struct isns_attr_list_parser *);
++extern void		isns_attr_list_parser_help(struct isns_attr_list_parser *);
++extern isns_object_template_t *isns_attr_list_parser_context(const struct isns_attr_list_parser *);
++extern int		isns_print_attrs(isns_object_t *, char **, unsigned int);
++
++#endif /* ISNS_ATTRS_H */
+diff --git a/utils/open-isns/authblock.c b/utils/open-isns/authblock.c
+new file mode 100644
+index 0000000..76d35b4
+--- /dev/null
++++ b/utils/open-isns/authblock.c
+@@ -0,0 +1,62 @@
++/*
++ * iSNS authentication functions
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include "isns.h"
++#include "attrs.h"
++#include "message.h"
++#include "util.h"
++
++/* We impose an artificial limit on the size of
++ * the size of the authenticator
++ */
++#define ISNS_SPISTR_MAX         512
++
++int
++isns_authblock_decode(buf_t *bp, struct isns_authblk *auth)
++{
++	unsigned int	avail = buf_avail(bp);
++
++	if (!buf_get32(bp, &auth->iab_bsd)
++	 || !buf_get32(bp, &auth->iab_length)
++	 || !buf_get64(bp, &auth->iab_timestamp)
++	 || !buf_get32(bp, &auth->iab_spi_len))
++		 return 0;
++
++	/* Make sure the length specified by the auth block
++	 * is reasonable. */
++	if (auth->iab_length < ISNS_AUTHBLK_SIZE
++	 || auth->iab_length > avail)
++		return 0;
++
++	/* This chops off any data trailing the auth block.
++	 * It also makes sure that we detect if iab_length
++	 * exceeds the amount of available data. */
++	if (!buf_truncate(bp, auth->iab_length - ISNS_AUTHBLK_SIZE))
++		return 0;
++
++	auth->iab_spi = buf_head(bp);
++	if (!buf_pull(bp, auth->iab_spi_len))
++		return 0;
++
++	auth->iab_sig = buf_head(bp);
++	auth->iab_sig_len = buf_avail(bp);
++	return 1;
++}
++
++int
++isns_authblock_encode(buf_t *bp, const struct isns_authblk *auth)
++{
++	if (!buf_put32(bp, auth->iab_bsd)
++	 || !buf_put32(bp, auth->iab_length)
++	 || !buf_put64(bp, auth->iab_timestamp)
++	 || !buf_put32(bp, auth->iab_spi_len)
++	 || !buf_put(bp, auth->iab_spi, auth->iab_spi_len)
++	 || !buf_put(bp, auth->iab_sig, auth->iab_sig_len))
++		return 0;
++	return 1;
++}
+diff --git a/utils/open-isns/bitvector.c b/utils/open-isns/bitvector.c
+new file mode 100644
+index 0000000..9d66276
+--- /dev/null
++++ b/utils/open-isns/bitvector.c
+@@ -0,0 +1,648 @@
++/*
++ * Handle bit vector as a run length encoded array of
++ * 32bit words.
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include "isns.h"
++#include "util.h"
++
++struct isns_bitvector {
++	unsigned int	ib_count;
++	uint32_t *	ib_words;
++};
++
++void
++isns_bitvector_init(isns_bitvector_t *bv)
++{
++	memset(bv, 0, sizeof(*bv));
++}
++
++void
++isns_bitvector_destroy(isns_bitvector_t *bv)
++{
++	isns_free(bv->ib_words);
++	memset(bv, 0, sizeof(*bv));
++}
++
++isns_bitvector_t *
++isns_bitvector_alloc(void)
++{
++	return isns_calloc(1, sizeof(isns_bitvector_t));
++}
++
++void
++isns_bitvector_free(isns_bitvector_t *bv)
++{
++	if (bv) {
++		isns_free(bv->ib_words);
++		memset(bv, 0xa5, sizeof(*bv));
++		isns_free(bv);
++	}
++}
++
++/*
++ * Helper function to locate bit
++ */
++uint32_t *
++__isns_bitvector_find_word(const isns_bitvector_t *bv, unsigned int bit)
++{
++	uint32_t	*wp, *end;
++
++	if (bv->ib_words == NULL)
++		return NULL;
++
++	wp = bv->ib_words;
++	end = wp + bv->ib_count;
++	while (wp < end) {
++		unsigned int	base, rlen;
++
++		base = wp[0];
++		rlen = wp[1];
++
++		isns_assert(!(base % 32));
++		if (base <= bit && bit < base + rlen * 32)
++			return wp + 2 + ((bit - base) / 32);
++
++		wp += 2 + rlen;
++		isns_assert(wp <= end);
++	}
++
++	return NULL;
++}
++
++/*
++ * Insert words in the middle of the array
++ */
++static inline void
++__isns_bitvector_insert_words(isns_bitvector_t *bv,
++		unsigned int offset, unsigned int count)
++{
++	bv->ib_words = isns_realloc(bv->ib_words,
++			(bv->ib_count + count) * sizeof(uint32_t));
++
++	/* If we insert in the middle, shift out the tail
++	 * to make room for the new range. */
++	isns_assert(offset <= bv->ib_count);
++	if (offset < bv->ib_count) {
++		memmove(bv->ib_words + offset + count,
++			bv->ib_words + offset,
++			(bv->ib_count - offset) * sizeof(uint32_t));
++	}
++
++	memset(bv->ib_words + offset, 0, count * sizeof(uint32_t));
++	bv->ib_count += count;
++}
++
++/*
++ * Insert a new range
++ */
++static inline uint32_t *
++__isns_bitvector_insert_range(isns_bitvector_t *bv,
++		unsigned int offset, unsigned int base)
++{
++	uint32_t	*pos;
++
++	__isns_bitvector_insert_words(bv, offset, 3);
++
++	pos = bv->ib_words + offset;
++
++	*pos++ = base & ~31;
++	*pos++ = 1;
++
++	return pos;
++}
++
++/*
++ * Extend an existing range
++ * @offset marks the beginning of the existing range.
++ */
++static inline uint32_t *
++__isns_bitvector_extend_range(isns_bitvector_t *bv,
++		unsigned int offset, unsigned int count)
++{
++	uint32_t	*pos, rlen;
++
++	/* Find the end of the range */
++	pos = bv->ib_words + offset;
++	rlen = pos[1];
++
++	__isns_bitvector_insert_words(bv, offset + 2 + rlen, count);
++
++	pos = bv->ib_words + offset;
++	pos[1] += count;
++
++	/* Return pointer to the last word of the new range. */
++	return pos + 2 + rlen + count - 1;
++}
++
++/*
++ * Find a suitable range for insertion
++ */
++static uint32_t *
++__isns_bitvector_find_insert_word(isns_bitvector_t *bv, unsigned int bit)
++{
++	uint32_t	*wp, *end;
++
++	if (bv->ib_words == NULL)
++		return __isns_bitvector_insert_range(bv, 0, bit);
++
++	wp = bv->ib_words;
++	end = wp + bv->ib_count;
++	while (wp < end) {
++		unsigned int	base, rlen, distance;
++
++		base = wp[0];
++		rlen = wp[1];
++
++		isns_assert(!(base % 32));
++
++		if (bit < base) {
++			return __isns_bitvector_insert_range(bv,
++					wp - bv->ib_words, bit);
++		}
++
++		distance = (bit - base) / 32;
++		if (distance < rlen) {
++			/* This bit is within range */
++			return wp + 2 + distance;
++		}
++
++		/* Is it efficient to extend this range?
++		 * The break even point is if we have to add
++		 * 3 words to extend the range, because a new
++		 * range would be at least that much.
++		 */
++		if (distance + 1 <= rlen + 3) {
++			return __isns_bitvector_extend_range(bv,
++					wp - bv->ib_words,
++					distance + 1 - rlen);
++		}
++
++		wp += 2 + rlen;
++		isns_assert(wp <= end);
++	}
++
++	/* No suitable range found. Append one at the end */
++	return __isns_bitvector_insert_range(bv,
++			bv->ib_count, bit);
++}
++
++/*
++ * After clearing a bit, check if the bitvector can be
++ * compacted.
++ */
++static void
++__isns_bitvector_compact(isns_bitvector_t *bv)
++{
++	uint32_t	*src, *dst, *end;
++	unsigned int	dst_base = 0, dst_len = 0;
++
++	if (bv->ib_words == NULL)
++		return;
++
++	src = dst = bv->ib_words;
++	end = src + bv->ib_count;
++	while (src < end) {
++		unsigned int	base, rlen;
++
++		base = *src++;
++		rlen = *src++;
++
++		/* Consume leading NUL words */
++		while (rlen && *src == 0) {
++			base += 32;
++			src++;
++			rlen--;
++		}
++
++		/* Consume trailing NUL words */
++		while (rlen && src[rlen-1] == 0)
++			rlen--;
++
++		if (rlen != 0) {
++			if (dst_len && dst_base + 32 * dst_len == base) {
++				/* We can extend the previous run */
++			} else {
++				/* New run. Close off the previous one,
++				 * if we had one. */
++				if (dst_len != 0) {
++					dst[0] = dst_base;
++					dst[1] = dst_len;
++					dst += 2 + dst_len;
++				}
++
++				dst_base = base;
++				dst_len = 0;
++			}
++
++			while (rlen--)
++				dst[2 + dst_len++] = *src++;
++		}
++
++		isns_assert(src <= end);
++	}
++
++
++	if (dst_len != 0) {
++		dst[0] = dst_base;
++		dst[1] = dst_len;
++		dst += 2 + dst_len;
++	}
++
++	bv->ib_count = dst - bv->ib_words;
++	if (bv->ib_count == 0)
++		isns_bitvector_destroy(bv);
++}
++
++/*
++ * Test the value of a single bit
++ */
++int
++isns_bitvector_test_bit(const isns_bitvector_t *bv, unsigned int bit)
++{
++	const uint32_t	*pos;
++	uint32_t	mask;
++
++	pos = __isns_bitvector_find_word(bv, bit);
++	if (pos == NULL)
++		return 0;
++
++	mask = 1 << (bit % 32);
++	return !!(*pos & mask);
++}
++
++int
++isns_bitvector_clear_bit(isns_bitvector_t *bv, unsigned int bit)
++{
++	uint32_t	*pos, oldval, mask;
++
++	pos = __isns_bitvector_find_word(bv, bit);
++	if (pos == NULL)
++		return 0;
++
++	mask = 1 << (bit % 32);
++	oldval = *pos;
++	*pos &= ~mask;
++
++	__isns_bitvector_compact(bv);
++	return !!(oldval & mask);
++}
++
++int
++isns_bitvector_set_bit(isns_bitvector_t *bv, unsigned int bit)
++{
++	uint32_t	*pos, oldval = 0, mask;
++
++	mask = 1 << (bit % 32);
++
++	pos = __isns_bitvector_find_insert_word(bv, bit);
++	if (pos != NULL) {
++		oldval = *pos;
++		*pos |= mask;
++
++		return !!(oldval & mask);
++	}
++
++	return 0;
++}
++
++int
++isns_bitvector_is_empty(const isns_bitvector_t *bv)
++{
++	uint32_t	*wp, *end;
++
++	if (bv == NULL || bv->ib_count == 0)
++		return 1;
++
++	/* In theory, we should never have a non-compacted
++	 * empty bitvector, as the only way to get one
++	 * is through clear_bit.
++	 * Better safe than sorry...
++	 */
++
++	wp = bv->ib_words;
++	end = wp + bv->ib_count;
++	while (wp < end) {
++		unsigned int	rlen;
++
++		rlen = wp[1];
++		wp += 2;
++
++		while (rlen--) {
++			if (*wp++)
++				return 0;
++		}
++		isns_assert(wp <= end);
++	}
++
++	return 1;
++}
++
++int
++isns_bitvector_intersect(const isns_bitvector_t *a,
++			const isns_bitvector_t *b,
++			isns_bitvector_t *result)
++{
++	const uint32_t	*runa, *runb, *enda, *endb;
++	const uint32_t	*wpa = NULL, *wpb = NULL;
++	uint32_t	bita = 0, lena = 0, bitb = 0, lenb = 0;
++	int		found = -1;
++
++	if (a == NULL || b == NULL)
++		return -1;
++
++	/* Returning the intersect is not implemented yet. */
++	isns_assert(result == NULL);
++
++	runa = a->ib_words;
++	enda = runa + a->ib_count;
++	runb = b->ib_words;
++	endb = runb + b->ib_count;
++
++	while (1) {
++		unsigned int	skip;
++
++		if (lena == 0) {
++next_a:
++			if (runa >= enda)
++				break;
++			bita = *runa++;
++			lena = *runa++;
++			wpa  = runa;
++			runa += lena;
++			lena *= 32;
++		}
++
++		if (lenb == 0) {
++next_b:
++			if (runb >= endb)
++				break;
++			bitb = *runb++;
++			lenb = *runb++;
++			wpb  = runb;
++			runb += lenb;
++			lenb *= 32;
++		}
++
++		if (bita < bitb) {
++			skip = bitb - bita;
++
++			/* range A ends before range B starts.
++			 * Proceed to next run in vector A. */
++			if (skip >= lena)
++				goto next_a;
++
++			bita += skip;
++			lena -= skip;
++			wpa  += skip / 32;
++		} else
++		if (bitb < bita) {
++			skip = bita - bitb;
++
++			/* range B ends before range A starts.
++			 * Proceed to next run in vector B. */
++			if (skip >= lenb)
++				goto next_b;
++
++			bitb += skip;
++			lenb -= skip;
++			wpb  += skip / 32;
++		}
++
++		isns_assert(bita == bitb);
++
++		while (lena && lenb) {
++			uint32_t intersect;
++
++			intersect = *wpa & *wpb;
++
++			if (!intersect)
++				goto next_word;
++
++			/* Find the bit */
++			if (found < 0) {
++				uint32_t mask = intersect;
++
++				found = bita;
++				while (!(mask & 1)) {
++					found++;
++					mask >>= 1;
++				}
++			}
++
++			if (result == NULL)
++				return found;
++
++			/* Append to result vector */
++			/* FIXME: TBD */
++
++next_word:
++			bita += 32; lena -= 32; wpa++;
++			bitb += 32; lenb -= 32; wpb++;
++		}
++	}
++
++	return found;
++}
++
++/*
++ * Iterate over the bit vector
++ */
++void
++isns_bitvector_foreach(const isns_bitvector_t *bv,
++		int (*cb)(uint32_t, void *),
++		void *user_data)
++{
++	uint32_t	*wp, *end;
++
++	wp = bv->ib_words;
++	end = wp + bv->ib_count;
++	while (wp < end) {
++		unsigned int	base, rlen;
++
++		base = wp[0];
++		rlen = wp[1];
++		wp += 2;
++
++		while (rlen--) {
++			uint32_t	mask, word;
++
++			word = *wp++;
++			for (mask = 1; mask; mask <<= 1, ++base) {
++				if (word & mask)
++					cb(base, user_data);
++			}
++		}
++		isns_assert(wp <= end);
++	}
++}
++
++void
++isns_bitvector_dump(const isns_bitvector_t *bv, isns_print_fn_t *fn)
++{
++	uint32_t	*wp, *end;
++
++	fn("Bit Vector %p (%u words):", bv, bv->ib_count);
++
++	wp = bv->ib_words;
++	end = wp + bv->ib_count;
++	while (wp < end) {
++		unsigned int	base, rlen;
++
++		base = wp[0];
++		rlen = wp[1];
++		wp += 2;
++
++		fn(" <%u:", base);
++		while (rlen--)
++			fn(" 0x%x", *wp++);
++		fn(">");
++
++		isns_assert(wp <= end);
++	}
++
++	if (bv->ib_count == 0)
++		fn("<empty>");
++	fn("\n");
++}
++
++static inline void
++__isns_bitvector_print_next(uint32_t first, uint32_t last,
++		isns_print_fn_t *fn)
++{
++	switch (last - first) {
++	case 0:
++		return;
++	case 1:
++		fn(", %u", last);
++		break;
++	default:
++		fn("-%u", last);
++		break;
++	}
++}
++
++void
++isns_bitvector_print(const isns_bitvector_t *bv,
++		isns_print_fn_t *fn)
++{
++	uint32_t	*wp, *end, first = 0, next = 0;
++	const char	*sepa = "";
++
++	wp = bv->ib_words;
++	end = wp + bv->ib_count;
++	while (wp < end) {
++		unsigned int	base, rlen;
++
++		base = wp[0];
++		rlen = wp[1];
++		wp += 2;
++
++		while (rlen--) {
++			uint32_t	mask, word;
++
++			word = *wp++;
++			for (mask = 1; mask; mask <<= 1, ++base) {
++				if (word & mask) {
++					if (next++)
++						continue;
++					fn("%s%u", sepa, base);
++					sepa = ", ";
++					first = base;
++					next = base + 1;
++				} else {
++					if (next)
++						__isns_bitvector_print_next(first, next - 1, fn);
++					first = next = 0;
++				}
++			}
++		}
++		isns_assert(wp <= end);
++	}
++
++	if (next)
++		__isns_bitvector_print_next(first, next - 1, fn);
++
++	if (*sepa == '\0')
++		fn("<empty>");
++	fn("\n");
++}
++
++#ifdef TEST
++int
++main(void)
++{
++	isns_bitvector_t	a, b;
++	int	i;
++
++	isns_bitvector_init(&a);
++	isns_bitvector_set_bit(&a, 0);
++	isns_bitvector_dump(&a, isns_print_stdout);
++	isns_bitvector_set_bit(&a, 1);
++	isns_bitvector_set_bit(&a, 16);
++	isns_bitvector_set_bit(&a, 32);
++	isns_bitvector_set_bit(&a, 64);
++	isns_bitvector_dump(&a, isns_print_stdout);
++	isns_bitvector_set_bit(&a, 8192);
++	isns_bitvector_set_bit(&a, 8196);
++	isns_bitvector_set_bit(&a, 8194);
++	isns_bitvector_dump(&a, isns_print_stdout);
++	isns_bitvector_set_bit(&a, 2052);
++	isns_bitvector_set_bit(&a, 2049);
++	isns_bitvector_set_bit(&a, 2051);
++	isns_bitvector_set_bit(&a, 2050);
++	isns_bitvector_dump(&a, isns_print_stdout);
++	isns_bitvector_print(&a, isns_print_stdout);
++	isns_bitvector_destroy(&a);
++
++	isns_bitvector_init(&a);
++	for (i = 127; i >= 0; --i)
++		isns_bitvector_set_bit(&a, i);
++	isns_bitvector_dump(&a, isns_print_stdout);
++	printf("[Compacting]\n");
++	__isns_bitvector_compact(&a);
++	isns_bitvector_dump(&a, isns_print_stdout);
++	isns_bitvector_print(&a, isns_print_stdout);
++	isns_bitvector_destroy(&a);
++
++	isns_bitvector_init(&a);
++	for (i = 0; i < 128; ++i)
++		isns_bitvector_set_bit(&a, i);
++	isns_bitvector_dump(&a, isns_print_stdout);
++	isns_bitvector_print(&a, isns_print_stdout);
++	isns_bitvector_destroy(&a);
++
++	isns_bitvector_init(&a);
++	isns_bitvector_init(&b);
++	isns_bitvector_set_bit(&a, 0);
++	isns_bitvector_set_bit(&a, 77);
++	isns_bitvector_set_bit(&a, 249);
++	isns_bitvector_set_bit(&a, 102);
++
++	isns_bitvector_set_bit(&b, 1);
++	isns_bitvector_set_bit(&b, 76);
++	isns_bitvector_set_bit(&b, 250);
++	isns_bitvector_set_bit(&b, 102);
++	i = isns_bitvector_intersect(&a, &b, NULL);
++	if (i != 102)
++		fprintf(stderr, "*** BAD: Intersect should return 102 (got %d)! ***\n", i);
++	else
++		printf("Intersect okay: %d\n", i);
++	isns_bitvector_destroy(&a);
++	isns_bitvector_destroy(&b);
++
++	isns_bitvector_init(&a);
++	isns_bitvector_set_bit(&a, 0);
++	isns_bitvector_set_bit(&a, 1);
++	isns_bitvector_clear_bit(&a, 1);
++	isns_bitvector_clear_bit(&a, 0);
++	isns_bitvector_dump(&a, isns_print_stdout);
++	isns_bitvector_print(&a, isns_print_stdout);
++	isns_bitvector_destroy(&a);
++	return 0;
++}
++#endif
+diff --git a/utils/open-isns/buffer.c b/utils/open-isns/buffer.c
+new file mode 100644
+index 0000000..279ab76
+--- /dev/null
++++ b/utils/open-isns/buffer.c
+@@ -0,0 +1,407 @@
++/*
++ * Buffer handling functions
++ *
++ * Copyright (C) 2003-2007, Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <err.h>
++#include <unistd.h>
++#include <netinet/in.h> /* ntohl&htonl */
++#include "buffer.h"
++#include "util.h"	/* htonll */
++
++static int	buf_drain(buf_t *bp);
++
++buf_t *
++buf_alloc(size_t size)
++{
++	buf_t	*bp;
++
++	bp = isns_calloc(1, sizeof(*bp));
++	buf_init_empty(bp, size);
++
++	return bp;
++}
++
++buf_t *
++buf_open(const char *filename, int flags)
++{
++	static const unsigned int buflen = 4096;
++	buf_t		*bp;
++	int		oerr;
++
++	if (!(bp = isns_calloc(1, sizeof(*bp) + buflen)))
++		return NULL;
++	buf_init(bp, (bp + 1), buflen);
++
++	switch (flags & O_ACCMODE) {
++	case O_RDONLY:
++		bp->write_mode = 0;
++		break;
++
++	case O_WRONLY:
++		bp->write_mode = 1;
++		break;
++
++	default:
++		errno = EINVAL;
++		goto failed;
++	}
++
++	if (!filename || !strcmp(filename, "-")) {
++		bp->fd = dup(bp->write_mode? 1 : 0);
++	} else {
++		bp->fd = open(filename, flags, 0666);
++	}
++
++	if (bp->fd < 0)
++		goto failed;
++
++	return bp;
++
++failed:	oerr = errno;
++	isns_free(bp);
++	errno = oerr;
++	return NULL;
++}
++
++buf_t *
++buf_dup(const buf_t *src)
++{
++	buf_t	*bp;
++
++	bp = buf_alloc(src->max_size);
++	buf_put(bp, src->base + src->head, src->tail - src->head);
++
++	bp->addr = src->addr;
++	bp->addrlen = src->addrlen;
++	return bp;
++}
++
++void
++buf_close(buf_t *bp)
++{
++	if (bp->write_mode)
++		buf_drain(bp);
++	if (bp->fd >= 0)
++		close(bp->fd);
++	bp->fd = -1;
++	isns_free(bp);
++}
++
++void
++buf_free(buf_t *bp)
++{
++	if (!bp)
++		return;
++	if (bp->allocated)
++		isns_free(bp->base);
++	isns_free(bp);
++}
++
++void
++buf_list_free(buf_t *bp)
++{
++	buf_t	*next;
++
++	while (bp) {
++		next = bp->next;
++		buf_free(bp);
++		bp = next;
++	}
++}
++
++void
++buf_init(buf_t *bp, void *mem, size_t len)
++{
++	memset(bp, 0, sizeof(*bp));
++	bp->base = (unsigned char *) mem;
++	bp->size = len;
++	bp->max_size = len;
++	bp->fd = -1;
++}
++
++void
++buf_init_empty(buf_t *bp, size_t len)
++{
++	memset(bp, 0, sizeof(*bp));
++	bp->max_size = len;
++	bp->fd = -1;
++}
++
++void
++buf_set(buf_t *bp, void *mem, size_t len)
++{
++	buf_init(bp, mem, len);
++	bp->tail = len;
++}
++
++void
++buf_clear(buf_t *bp)
++{
++	bp->head = bp->tail = 0;
++}
++
++int
++buf_fill(buf_t *bp)
++{
++	int	n;
++
++	if (bp->head || bp->tail)
++		buf_compact(bp);
++
++	if (bp->write_mode || bp->fd < 0)
++		return 0;
++
++	n = read(bp->fd, bp->base + bp->tail, buf_tailroom(bp));
++	if (n < 0) {
++		warn("read error");
++		return 0;
++	}
++
++	bp->tail += n;
++	return n;
++}
++
++int
++buf_drain(buf_t *bp)
++{
++	int	n;
++
++	if (!bp->write_mode || bp->fd < 0)
++		return 0;
++
++	n = write(bp->fd, bp->base + bp->head, buf_avail(bp));
++	if (n < 0) {
++		warn("write error");
++		return 0;
++	}
++
++	bp->head += n;
++	return n;
++}
++
++int
++__buf_resize(buf_t *bp, size_t new_size)
++{
++	void *new_base;
++
++	if (new_size > bp->max_size)
++		return 0;
++	isns_assert(bp->allocated || bp->base == NULL);
++
++	new_size = (new_size + 127) & ~127;
++	if (new_size > bp->max_size)
++		new_size = bp->max_size;
++
++	new_base = isns_realloc(bp->base, new_size);
++	if (new_base == NULL)
++		return 0;
++
++	bp->base = new_base;
++	bp->size = new_size;
++	bp->allocated = 1;
++	return new_size;
++}
++
++buf_t *
++buf_split(buf_t **to_split, size_t size)
++{
++	buf_t *old = *to_split, *new;
++	size_t avail;
++
++	avail = buf_avail(old);
++	if (size > avail)
++		return NULL;
++
++	if (size == avail) {
++		*to_split = NULL;
++		return old;
++	}
++
++	new = buf_alloc(size);
++	buf_put(new, buf_head(old), size);
++	buf_pull(old, size);
++
++	return new;
++}
++
++int
++buf_seek(buf_t *bp, off_t offset)
++{
++	if (bp->write_mode && !buf_drain(bp))
++		return 0;
++	if (lseek(bp->fd, offset, SEEK_SET) < 0) {
++		warn("cannot seek to offset %ld", (long) offset);
++		return 0;
++	}
++	return 1;
++}
++
++int
++buf_get(buf_t *bp, void *mem, size_t len)
++{
++	caddr_t		dst = (caddr_t) mem;
++	unsigned int	total = len, copy;
++
++	while (len) {
++		if ((copy = buf_avail(bp)) > len)
++			copy = len;
++		if (copy == 0) {
++			if (!buf_fill(bp))
++				return 0;
++			continue;
++		}
++		if (dst) {
++			memcpy(dst, bp->base + bp->head, copy);
++			dst += copy;
++		}
++		bp->head += copy;
++		len -= copy;
++	}
++	return total;
++}
++
++int
++buf_get32(buf_t *bp, uint32_t *vp)
++{
++	if (!buf_get(bp, vp, 4))
++		return 0;
++	*vp = ntohl(*vp);
++	return 1;
++}
++
++int
++buf_get64(buf_t *bp, uint64_t *vp)
++{
++	if (!buf_get(bp, vp, 8))
++		return 0;
++	*vp = ntohll(*vp);
++	return 1;
++}
++
++int
++buf_gets(buf_t *bp, char *stringbuf, size_t size)
++{
++	uint32_t	len, copy;
++
++	if (size == 0)
++		return 0;
++
++	if (!buf_get32(bp, &len))
++		return 0;
++
++	if ((copy = len) >= size)
++		copy = size - 1;
++
++	if (!buf_get(bp, stringbuf, copy))
++		return 0;
++	stringbuf[copy] = '\0';
++
++	/* Pull remaining bytes */
++	if (copy != len && !buf_pull(bp, len - copy))
++		return 0;
++
++	return copy + 1;
++}
++
++int
++buf_put(buf_t *bp, const void *mem, size_t len)
++{
++	caddr_t		src = (caddr_t) mem;
++	unsigned int	total = len, copy;
++
++	while (len) {
++		if ((copy = bp->size - bp->tail) > len)
++			copy = len;
++		if (copy == 0) {
++			if (buf_drain(bp)) {
++				buf_compact(bp);
++				continue;
++			}
++			if (__buf_resize(bp, bp->tail + len)) {
++				buf_compact(bp);
++				continue;
++			}
++			return 0;
++		}
++		if (src) {
++			memcpy(bp->base + bp->tail, src, copy);
++			src += copy;
++		}
++		bp->tail += copy;
++		len -= copy;
++	}
++	return total;
++}
++
++int
++buf_putc(buf_t *bp, int byte)
++{
++	unsigned char	c = byte;
++
++	return buf_put(bp, &c, 1);
++}
++
++int
++buf_put32(buf_t *bp, uint32_t val)
++{
++	val = htonl(val);
++	if (!buf_put(bp, &val, 4))
++		return 0;
++	return 1;
++}
++
++int
++buf_put64(buf_t *bp, uint64_t val)
++{
++	val = htonll(val);
++	return buf_put(bp, &val, 8);
++}
++
++int
++buf_puts(buf_t *bp, const char *sp)
++{
++	uint32_t	len = 0;
++
++	if (sp)
++		len = strlen(sp);
++	return buf_put32(bp, len) && buf_put(bp, sp, len);
++}
++
++void
++buf_compact(buf_t *bp)
++{
++	unsigned int	count;
++
++	if (bp->head == 0)
++		return;
++
++	count = bp->tail - bp->head;
++	memmove(bp->base, bp->base + bp->head, count);
++	bp->tail -= bp->head;
++	bp->head  = 0;
++}
++
++void
++buf_list_append(buf_t **list, buf_t *bp)
++{
++	bp->next = NULL;
++	while (*list)
++		list = &(*list)->next;
++	*list = bp;
++}
++
++int
++buf_truncate(buf_t *bp, size_t len)
++{
++	if (bp->head + len > bp->tail)
++		return 0;
++
++	bp->tail = bp->head + len;
++	return 1;
++}
+diff --git a/utils/open-isns/buffer.h b/utils/open-isns/buffer.h
+new file mode 100644
+index 0000000..75ba910
+--- /dev/null
++++ b/utils/open-isns/buffer.h
+@@ -0,0 +1,141 @@
++/*
++ * Buffer handling functions
++ *
++ * Copyright (C) 2003-2006, Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef BUFFER_H
++#define BUFFER_H
++
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <stdint.h>
++
++typedef struct isns_buf {
++	struct isns_buf *	next;
++	unsigned char *		base;
++	unsigned int		head, tail, size, max_size;
++	unsigned int		write_mode : 1,
++				allocated  : 1;
++	int			fd;
++
++	/* Anonymous union for misc stuff */
++	union {
++		struct {
++			struct sockaddr_storage addr;
++			socklen_t addrlen;
++		};
++	};
++} buf_t;
++
++extern buf_t *		buf_open(const char *, int);
++extern buf_t *		buf_alloc(size_t);
++extern buf_t *		buf_dup(const buf_t *);
++extern void		buf_init(buf_t *, void *, size_t);
++extern void		buf_init_empty(buf_t *, size_t);
++extern void		buf_set(buf_t *, void *, size_t);
++
++extern void		buf_clear(buf_t *);
++extern void		buf_close(buf_t *);
++extern void		buf_destroy(buf_t *);
++extern void		buf_free(buf_t *);
++extern void		buf_list_free(buf_t *);
++
++extern int		buf_get(buf_t *, void *, size_t);
++extern int		buf_get32(buf_t *, uint32_t *);
++extern int		buf_get64(buf_t *, uint64_t *);
++extern int		buf_gets(buf_t *, char *, size_t);
++extern int		buf_put(buf_t *, const void *, size_t);
++extern int		buf_put32(buf_t *, uint32_t);
++extern int		buf_put64(buf_t *, uint64_t);
++extern int		buf_puts(buf_t *, const char *);
++extern int		buf_putc(buf_t *, int);
++extern int		buf_read(buf_t *, int);
++extern int		buf_seek(buf_t *bp, off_t offset);
++extern int		buf_truncate(buf_t *, size_t);
++extern void		buf_compact(buf_t *);
++extern buf_t *		buf_split(buf_t **to_split, size_t len);
++extern int		__buf_resize(buf_t *, size_t);
++
++extern void		buf_list_append(buf_t **, buf_t *);
++
++static inline size_t
++buf_avail(const buf_t *bp)
++{
++	return bp->tail - bp->head;
++}
++
++static inline size_t
++buf_tailroom(const buf_t *bp)
++{
++	return bp->max_size - bp->tail;
++}
++
++static inline size_t
++buf_size(const buf_t *bp)
++{
++	return bp->size;
++}
++
++static inline void *
++buf_head(const buf_t *bp)
++{
++	return bp->base + bp->head;
++}
++
++static inline void *
++buf_tail(const buf_t *bp)
++{
++	return bp->base + bp->tail;
++}
++
++static inline int
++buf_reserve(buf_t *bp, size_t len)
++{
++	if (bp->head != bp->tail)
++		return 0;
++	if (bp->max_size - bp->head < len)
++		return 0;
++	bp->head += len;
++	bp->tail += len;
++	return 1;
++}
++
++static inline int
++buf_pull(buf_t *bp, size_t len)
++{
++	if (len > buf_avail(bp))
++		return 0;
++	bp->head += len;
++	return 1;
++}
++
++static inline void *
++buf_push(buf_t *bp, size_t len)
++{
++	if (bp->max_size - bp->tail < len)
++		return NULL;
++
++	if (bp->tail + len > bp->size
++	 && !__buf_resize(bp, bp->tail + len))
++		return NULL;
++
++	bp->tail += len;
++	return bp->base + bp->tail - len;
++}
++
++static inline void *
++buf_push_head(buf_t *bp, size_t len)
++{
++	if (bp->head < len)
++		return NULL;
++
++	if (bp->tail > bp->size
++	 && !__buf_resize(bp, bp->tail))
++		return NULL;
++
++	bp->head -= len;
++	return bp->base + bp->head;
++}
++
++#endif /* BUFFER_H */
+diff --git a/utils/open-isns/callback.c b/utils/open-isns/callback.c
+new file mode 100644
+index 0000000..ecdabd7
+--- /dev/null
++++ b/utils/open-isns/callback.c
+@@ -0,0 +1,148 @@
++/*
++ * iSNS object callbacks for SCN and other stuff
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include "isns.h"
++#include "objects.h"
++#include "vendor.h"
++#include "attrs.h"
++#include "util.h"
++
++typedef struct isns_object_notifier isns_object_notifier_t;
++struct isns_object_notifier {
++	isns_list_t		list;
++	isns_db_callback_t *	func;
++	void *			data;
++};
++
++typedef struct isns_cb_event isns_cb_event_t;
++struct isns_cb_event {
++	isns_list_t		list;
++	isns_db_event_t		info;
++};
++
++static ISNS_LIST_DECLARE(notifiers);
++static ISNS_LIST_DECLARE(events);
++
++static inline void
++__isns_db_event(isns_object_t *dst,
++		isns_object_t *obj,
++		unsigned int bits,
++		isns_object_t *trigger)
++{
++	isns_cb_event_t *ev;
++	
++	ev = isns_calloc(1, sizeof(*ev));
++	ev->info.ie_recipient = isns_object_get(dst);
++	ev->info.ie_object = isns_object_get(obj);
++	ev->info.ie_bits = bits;
++	ev->info.ie_trigger = isns_object_get(trigger);
++	isns_list_append(&events, &ev->list);
++}
++
++void
++isns_object_event(isns_object_t *obj,
++		unsigned int bits,
++		isns_object_t *trigger)
++{
++	__isns_db_event(NULL, obj, bits, trigger);
++}
++
++void
++isns_unicast_event(isns_object_t *dst,
++		isns_object_t *obj,
++		unsigned int bits,
++		isns_object_t *trigger)
++{
++	__isns_db_event(dst, obj, bits, trigger);
++}
++
++/*
++ * Given an object pair and an event bitmask,
++ * invoke all callbacks
++ */
++static inline void
++isns_call_callbacks(isns_db_event_t *ev)
++{
++	isns_object_t	*obj = ev->ie_object;
++	isns_list_t	*pos, *next;
++
++	ev->ie_bits |= obj->ie_scn_bits;
++	if (ev->ie_bits == 0)
++		return;
++	isns_list_foreach(&notifiers, pos, next) {
++		isns_object_notifier_t *not;
++
++		not = isns_list_item(isns_object_notifier_t, list, pos);
++		not->func(ev, not->data);
++	}
++	obj->ie_scn_bits = 0;
++}
++
++void
++isns_flush_events(void)
++{
++	while (!isns_list_empty(&events)) {
++		isns_cb_event_t *ev = isns_list_item(isns_cb_event_t, list, events.next);
++
++		isns_call_callbacks(&ev->info);
++		isns_object_release(ev->info.ie_recipient);
++		isns_object_release(ev->info.ie_object);
++		isns_object_release(ev->info.ie_trigger);
++		isns_list_del(&ev->list);
++		isns_free(ev);
++	}
++}
++
++void
++isns_register_callback(isns_db_callback_t *func,
++				void *user_data)
++{
++	isns_object_notifier_t *not;
++
++	not = isns_calloc(1, sizeof(*not));
++	not->func = func;
++	not->data = user_data;
++
++	isns_list_append(&notifiers, &not->list);
++}
++
++const char *
++isns_event_string(unsigned int bits)
++{
++	static const char *names[16] = {
++	[ISNS_SCN_DD_MEMBER_ADDED]	= "member added",
++	[ISNS_SCN_DD_MEMBER_REMOVED]	= "member removed",
++	[ISNS_SCN_OBJECT_UPDATED]	= "updated",
++	[ISNS_SCN_OBJECT_ADDED]		= "added",
++	[ISNS_SCN_OBJECT_REMOVED]	= "removed",
++	[ISNS_SCN_MANAGEMENT_REGISTRATION]= "mgmt registration",
++	[ISNS_SCN_TARGET_AND_SELF_ONLY]	= "target+self",
++	[ISNS_SCN_INITIATOR_AND_SELF_ONLY]= "initiator+self",
++	};
++	static char	buffer[128];
++	unsigned int	pos = 0, i;
++
++
++	for (i = 0; i < 16; ++i, bits >>= 1) {
++		if (!(bits & 1))
++			continue;
++
++		if (names[i]) {
++			snprintf(buffer + pos, sizeof(buffer) - pos,
++				"%s%s", pos? ", " : "", names[i]);
++		} else {
++			snprintf(buffer + pos, sizeof(buffer) - pos,
++				"%sevent %u", pos? ", " : "", i);
++		}
++		pos = strlen(buffer);
++	}
++	if (pos == 0)
++		return "<no event>";
++
++	return buffer;
++}
+diff --git a/utils/open-isns/client.c b/utils/open-isns/client.c
+new file mode 100644
+index 0000000..c470048
+--- /dev/null
++++ b/utils/open-isns/client.c
+@@ -0,0 +1,205 @@
++/*
++ * Client functions
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <getopt.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <signal.h>
++#include <unistd.h>
++
++#include <isns.h>
++#include "security.h"
++#include "util.h"
++#include "internal.h"
++#include "config.h"
++
++static isns_client_t *
++__isns_create_default_client(isns_socket_t *sock, isns_security_t *ctx,
++		const char *source_name)
++{
++	isns_client_t	*clnt;
++
++	clnt = isns_calloc(1, sizeof(*clnt));
++
++	if (!source_name)
++		source_name = isns_config.ic_source_name;
++
++	clnt->ic_source = isns_source_create_iscsi(source_name);
++	clnt->ic_socket = sock;
++
++	isns_socket_set_security_ctx(clnt->ic_socket, ctx);
++
++	return clnt;
++}
++
++isns_client_t *
++isns_create_client(isns_security_t *ctx, const char *source_name)
++{
++	isns_socket_t	*sock;
++	const char	*server_name;
++
++	server_name = isns_config.ic_server_name;
++	if (!strcasecmp(server_name, "SLP:")
++	 && !(server_name = isns_slp_find())) {
++		isns_error("Unable to locate iSNS server through SLP\n");
++		return NULL;
++	}
++
++	sock = isns_create_bound_client_socket(
++			isns_config.ic_bind_address,
++			server_name,
++			"isns", 0, SOCK_STREAM);
++	if (sock == NULL) {
++		isns_error("Unable to create socket for host \"%s\"\n",
++			isns_config.ic_server_name);
++		return NULL;
++	}
++
++	return __isns_create_default_client(sock,
++			ctx? : isns_default_security_context(0),
++			source_name);
++}
++
++isns_client_t *
++isns_create_default_client(isns_security_t *ctx)
++{
++	return isns_create_client(ctx, isns_config.ic_source_name);
++}
++
++isns_client_t *
++isns_create_local_client(isns_security_t *ctx, const char *source_name)
++{
++	isns_socket_t	*sock;
++
++	if (isns_config.ic_control_socket == NULL)
++		isns_fatal("Cannot use local mode: no local control socket\n");
++
++	sock = isns_create_client_socket(isns_config.ic_control_socket,
++			NULL, 0, SOCK_STREAM);
++	if (sock == NULL) {
++		isns_error("Unable to create control socket (%s)\n",
++			isns_config.ic_control_socket);
++		return NULL;
++	}
++
++	return __isns_create_default_client(sock, ctx, source_name);
++}
++
++int
++isns_client_call(isns_client_t *clnt,
++		isns_simple_t **inout)
++{
++	return isns_simple_call(clnt->ic_socket, inout);
++}
++
++void
++isns_client_destroy(isns_client_t *clnt)
++{
++	if (clnt->ic_socket)
++		isns_socket_free(clnt->ic_socket);
++	if (clnt->ic_source)
++		isns_source_release(clnt->ic_source);
++	isns_free(clnt);
++}
++
++/*
++ * Get the local address
++ */
++int
++isns_client_get_local_address(const isns_client_t *clnt,
++				isns_portal_info_t *portal_info)
++{
++	return isns_socket_get_portal_info(clnt->ic_socket, portal_info);
++}
++
++/*
++ * Create a security context
++ */
++static isns_security_t *
++__create_security_context(const char *name, const char *auth_key,
++		const char *server_key)
++{
++#ifdef WITH_SECURITY
++	isns_security_t 	*ctx;
++	isns_principal_t	*princ;
++#endif /* WITH_SECURITY */
++
++	if (!isns_config.ic_security)
++		return NULL;
++
++#ifndef WITH_SECURITY
++	isns_error("Cannot create security context: security disabled at build time\n");
++	return NULL;
++#else /* WITH_SECURITY */
++	ctx = isns_create_dsa_context();
++	if (ctx == NULL)
++		isns_fatal("Unable to create security context\n");
++
++	/* Load my own key */
++	princ = isns_security_load_privkey(ctx, auth_key);
++	if (!princ)
++		isns_fatal("Unable to load private key from %s\n",
++				auth_key);
++
++	isns_principal_set_name(princ, name);
++	isns_security_set_identity(ctx, princ);
++
++	if (server_key) {
++		/* We're a client, and we want to load the
++		 * server's public key in order to authenticate
++		 * the server's responses.
++		 */
++		princ = isns_security_load_pubkey(ctx, server_key);
++		if (!princ)
++			isns_fatal("Unable to load public key from %s\n",
++					server_key);
++
++		/* Do *not* set a name for this principal -
++		 * this will be the default principal used when
++		 * verifying the server's reply, which is a good thing
++		 * because we don't know what SPI the server will
++		 * be using. */
++		isns_add_principal(ctx, princ);
++
++		/* But set a policy for the server which allows it
++		   to send ESI and SCN messages */
++		isns_principal_set_policy(princ, isns_policy_server());
++	}
++
++	return ctx;
++#endif /* WITH_SECURITY */
++}
++
++/*
++ * Create the default security context
++ */
++isns_security_t *
++isns_default_security_context(int server_only)
++{
++	static isns_security_t 	*ctx;
++
++	if (ctx == NULL)
++		ctx = __create_security_context(isns_config.ic_auth_name,
++				isns_config.ic_auth_key_file,
++				server_only? NULL : isns_config.ic_server_key_file);
++	return ctx;
++}
++
++/*
++ * Create the control security context
++ */
++isns_security_t *
++isns_control_security_context(int server_only)
++{
++	static isns_security_t 	*ctx;
++
++	if (ctx == NULL)
++		ctx = __create_security_context(isns_config.ic_control_name,
++				isns_config.ic_control_key_file,
++				server_only? NULL : isns_config.ic_server_key_file);
++	return ctx;
++}
+diff --git a/utils/open-isns/compat/my_getopt.c b/utils/open-isns/compat/my_getopt.c
+new file mode 100644
+index 0000000..512b0ae
+--- /dev/null
++++ b/utils/open-isns/compat/my_getopt.c
+@@ -0,0 +1,271 @@
++/*
++ *  my_getopt.c - my re-implementation of getopt.
++ *  Copyright 1997, 2000, 2001, 2002, Benjamin Sittler
++ *
++ *  Permission is hereby granted, free of charge, to any person
++ *  obtaining a copy of this software and associated documentation
++ *  files (the "Software"), to deal in the Software without
++ *  restriction, including without limitation the rights to use, copy,
++ *  modify, merge, publish, distribute, sublicense, and/or sell copies
++ *  of the Software, and to permit persons to whom the Software is
++ *  furnished to do so, subject to the following conditions:
++ *  
++ *  The above copyright notice and this permission notice shall be
++ *  included in all copies or substantial portions of the Software.
++ *  
++ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ *  NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
++ *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ *  DEALINGS IN THE SOFTWARE.
++ */
++
++#include <sys/types.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include "my_getopt.h"
++
++int my_optind=1, my_opterr=1, my_optopt=0;
++char *my_optarg=0;
++
++/* this is the plain old UNIX getopt, with GNU-style extensions. */
++/* if you're porting some piece of UNIX software, this is all you need. */
++/* this supports GNU-style permution and optional arguments */
++
++int my_getopt(int argc, char * argv[], const char *opts)
++{
++  static int charind=0;
++  const char *s;
++  char mode, colon_mode;
++  int off = 0, opt = -1;
++
++  if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+';
++  else {
++    if((colon_mode = *opts) == ':') off ++;
++    if(((mode = opts[off]) == '+') || (mode == '-')) {
++      off++;
++      if((colon_mode != ':') && ((colon_mode = opts[off]) == ':'))
++        off ++;
++    }
++  }
++  my_optarg = 0;
++  if(charind) {
++    my_optopt = argv[my_optind][charind];
++    for(s=opts+off; *s; s++) if(my_optopt == *s) {
++      charind++;
++      if((*(++s) == ':') || ((my_optopt == 'W') && (*s == ';'))) {
++        if(argv[my_optind][charind]) {
++          my_optarg = &(argv[my_optind++][charind]);
++          charind = 0;
++        } else if(*(++s) != ':') {
++          charind = 0;
++          if(++my_optind >= argc) {
++            if(my_opterr) fprintf(stderr,
++                                "%s: option requires an argument -- %c\n",
++                                argv[0], my_optopt);
++            opt = (colon_mode == ':') ? ':' : '?';
++            goto my_getopt_ok;
++          }
++          my_optarg = argv[my_optind++];
++        }
++      }
++      opt = my_optopt;
++      goto my_getopt_ok;
++    }
++    if(my_opterr) fprintf(stderr,
++                        "%s: illegal option -- %c\n",
++                        argv[0], my_optopt);
++    opt = '?';
++    if(argv[my_optind][++charind] == '\0') {
++      my_optind++;
++      charind = 0;
++    }
++  my_getopt_ok:
++    if(charind && ! argv[my_optind][charind]) {
++      my_optind++;
++      charind = 0;
++    }
++  } else if((my_optind >= argc) ||
++             ((argv[my_optind][0] == '-') &&
++              (argv[my_optind][1] == '-') &&
++              (argv[my_optind][2] == '\0'))) {
++    my_optind++;
++    opt = -1;
++  } else if((argv[my_optind][0] != '-') ||
++             (argv[my_optind][1] == '\0')) {
++    char *tmp;
++    int i, j, k;
++
++    if(mode == '+') opt = -1;
++    else if(mode == '-') {
++      my_optarg = argv[my_optind++];
++      charind = 0;
++      opt = 1;
++    } else {
++      for(i=j=my_optind; i<argc; i++) if((argv[i][0] == '-') &&
++                                        (argv[i][1] != '\0')) {
++        my_optind=i;
++        opt=my_getopt(argc, argv, opts);
++        while(i > j) {
++          tmp=argv[--i];
++          for(k=i; k+1<my_optind; k++) argv[k]=argv[k+1];
++          argv[--my_optind]=tmp;
++        }
++        break;
++      }
++      if(i == argc) opt = -1;
++    }
++  } else {
++    charind++;
++    opt = my_getopt(argc, argv, opts);
++  }
++  if (my_optind > argc) my_optind = argc;
++  return opt;
++}
++
++/* this is the extended getopt_long{,_only}, with some GNU-like
++ * extensions. Implements _getopt_internal in case any programs
++ * expecting GNU libc getopt call it.
++ */
++
++int _my_getopt_internal(int argc, char * argv[], const char *shortopts,
++                     const struct option *longopts, int *longind,
++                     int long_only)
++{
++  char mode, colon_mode = *shortopts;
++  int shortoff = 0, opt = -1;
++
++  if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+';
++  else {
++    if((colon_mode = *shortopts) == ':') shortoff ++;
++    if(((mode = shortopts[shortoff]) == '+') || (mode == '-')) {
++      shortoff++;
++      if((colon_mode != ':') && ((colon_mode = shortopts[shortoff]) == ':'))
++        shortoff ++;
++    }
++  }
++  my_optarg = 0;
++  if((my_optind >= argc) ||
++      ((argv[my_optind][0] == '-') &&
++       (argv[my_optind][1] == '-') &&
++       (argv[my_optind][2] == '\0'))) {
++    my_optind++;
++    opt = -1;
++  } else if((argv[my_optind][0] != '-') ||
++            (argv[my_optind][1] == '\0')) {
++    char *tmp;
++    int i, j, k;
++
++    opt = -1;
++    if(mode == '+') return -1;
++    else if(mode == '-') {
++      my_optarg = argv[my_optind++];
++      return 1;
++    }
++    for(i=j=my_optind; i<argc; i++) if((argv[i][0] == '-') &&
++                                    (argv[i][1] != '\0')) {
++      my_optind=i;
++      opt=_my_getopt_internal(argc, argv, shortopts,
++                              longopts, longind,
++                              long_only);
++      while(i > j) {
++        tmp=argv[--i];
++        for(k=i; k+1<my_optind; k++)
++          argv[k]=argv[k+1];
++        argv[--my_optind]=tmp;
++      }
++      break;
++    }
++  } else if((!long_only) && (argv[my_optind][1] != '-'))
++    opt = my_getopt(argc, argv, shortopts);
++  else {
++    int charind, offset;
++    int found = 0, ind, hits = 0;
++
++    if(((my_optopt = argv[my_optind][1]) != '-') && ! argv[my_optind][2]) {
++      int c;
++      
++      ind = shortoff;
++      while((c = shortopts[ind++])) {
++        if(((shortopts[ind] == ':') ||
++            ((c == 'W') && (shortopts[ind] == ';'))) &&
++           (shortopts[++ind] == ':'))
++          ind ++;
++        if(my_optopt == c) return my_getopt(argc, argv, shortopts);
++      }
++    }
++    offset = 2 - (argv[my_optind][1] != '-');
++    for(charind = offset;
++        (argv[my_optind][charind] != '\0') &&
++          (argv[my_optind][charind] != '=');
++        charind++);
++    for(ind = 0; longopts[ind].name && !hits; ind++)
++      if((strlen(longopts[ind].name) == (size_t) (charind - offset)) &&
++         (strncmp(longopts[ind].name,
++                  argv[my_optind] + offset, charind - offset) == 0))
++        found = ind, hits++;
++    if(!hits) for(ind = 0; longopts[ind].name; ind++)
++      if(strncmp(longopts[ind].name,
++                 argv[my_optind] + offset, charind - offset) == 0)
++        found = ind, hits++;
++    if(hits == 1) {
++      opt = 0;
++
++      if(argv[my_optind][charind] == '=') {
++        if(longopts[found].has_arg == 0) {
++          opt = '?';
++          if(my_opterr) fprintf(stderr,
++                             "%s: option `--%s' doesn't allow an argument\n",
++                             argv[0], longopts[found].name);
++        } else {
++          my_optarg = argv[my_optind] + ++charind;
++          charind = 0;
++        }
++      } else if(longopts[found].has_arg == 1) {
++        if(++my_optind >= argc) {
++          opt = (colon_mode == ':') ? ':' : '?';
++          if(my_opterr) fprintf(stderr,
++                             "%s: option `--%s' requires an argument\n",
++                             argv[0], longopts[found].name);
++        } else my_optarg = argv[my_optind];
++      }
++      if(!opt) {
++        if (longind) *longind = found;
++        if(!longopts[found].flag) opt = longopts[found].val;
++        else *(longopts[found].flag) = longopts[found].val;
++      }
++      my_optind++;
++    } else if(!hits) {
++      if(offset == 1) opt = my_getopt(argc, argv, shortopts);
++      else {
++        opt = '?';
++        if(my_opterr) fprintf(stderr,
++                           "%s: unrecognized option `%s'\n",
++                           argv[0], argv[my_optind++]);
++      }
++    } else {
++      opt = '?';
++      if(my_opterr) fprintf(stderr,
++                         "%s: option `%s' is ambiguous\n",
++                         argv[0], argv[my_optind++]);
++    }
++  }
++  if (my_optind > argc) my_optind = argc;
++  return opt;
++}
++
++int my_getopt_long(int argc, char * argv[], const char *shortopts,
++                const struct option *longopts, int *longind)
++{
++  return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 0);
++}
++
++int my_getopt_long_only(int argc, char * argv[], const char *shortopts,
++                const struct option *longopts, int *longind)
++{
++  return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 1);
++}
+diff --git a/utils/open-isns/compat/my_getopt.h b/utils/open-isns/compat/my_getopt.h
+new file mode 100644
+index 0000000..34fcfe7
+--- /dev/null
++++ b/utils/open-isns/compat/my_getopt.h
+@@ -0,0 +1,69 @@
++/*
++ *  my_getopt.h - interface to my re-implementation of getopt.
++ *  Copyright 1997, 2000, 2001, 2002, Benjamin Sittler
++ *
++ *  Permission is hereby granted, free of charge, to any person
++ *  obtaining a copy of this software and associated documentation
++ *  files (the "Software"), to deal in the Software without
++ *  restriction, including without limitation the rights to use, copy,
++ *  modify, merge, publish, distribute, sublicense, and/or sell copies
++ *  of the Software, and to permit persons to whom the Software is
++ *  furnished to do so, subject to the following conditions:
++ *  
++ *  The above copyright notice and this permission notice shall be
++ *  included in all copies or substantial portions of the Software.
++ *  
++ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ *  NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
++ *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ *  DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef MY_GETOPT_H_INCLUDED
++#define MY_GETOPT_H_INCLUDED
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/* UNIX-style short-argument parser */
++extern int my_getopt(int argc, char * argv[], const char *opts);
++
++extern int my_optind, my_opterr, my_optopt;
++extern char *my_optarg;
++
++struct option {
++  const char *name;
++  int has_arg;
++  int *flag;
++  int val;
++};
++
++/* human-readable values for has_arg */
++#undef no_argument
++#define no_argument 0
++#undef required_argument
++#define required_argument 1
++#undef optional_argument
++#define optional_argument 2
++
++/* GNU-style long-argument parsers */
++extern int my_getopt_long(int argc, char * argv[], const char *shortopts,
++                       const struct option *longopts, int *longind);
++
++extern int my_getopt_long_only(int argc, char * argv[], const char *shortopts,
++                            const struct option *longopts, int *longind);
++
++extern int _my_getopt_internal(int argc, char * argv[], const char *shortopts,
++                            const struct option *longopts, int *longind,
++                            int long_only);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* MY_GETOPT_H_INCLUDED */
+diff --git a/utils/open-isns/config.c b/utils/open-isns/config.c
+new file mode 100644
+index 0000000..cc470a4
+--- /dev/null
++++ b/utils/open-isns/config.c
+@@ -0,0 +1,278 @@
++/*
++ * Config file reader
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <ctype.h>
++
++#include "isns.h"
++#include "util.h"
++#include "paths.h"
++
++/*
++ * iSNS configuration
++ */
++struct isns_config	isns_config = {
++	/* Security parameters */
++	.ic_security		= -1,
++	.ic_auth_key_file	= ISNS_ETCDIR "/auth_key",
++	.ic_server_key_file	= ISNS_ETCDIR "/server_key.pub",
++	.ic_client_keystore	= "DB:",
++	.ic_control_socket	= ISNS_RUNDIR "/isnsctl",
++	.ic_pidfile		= ISNS_RUNDIR "/isnsd.pid",
++	.ic_local_registry_file	= ISNS_DEFAULT_LOCAL_REGISTRY,
++
++	.ic_control_name	= "isns.control",
++	.ic_control_key_file	= ISNS_ETCDIR "/control.key",
++
++	.ic_registration_period = 3600,		/* 1 hour */
++	.ic_scn_timeout		= 60,
++	.ic_scn_retries		= 3,
++
++	.ic_esi_max_interval	= 600,		/* 10 minutes */
++	.ic_esi_min_interval	= 60,		/* 1 minute */
++	.ic_esi_retries		= 3,
++
++	.ic_auth = {
++		.replay_window = 300,		/* 5 min clock skew */
++		.timestamp_jitter = 1,		/* 1 sec timestamp jitter */
++		.allow_unknown_peers = 1,
++	},
++	.ic_network = {
++		.max_sockets = 1024,
++		.connect_timeout = 5,
++		.reconnect_timeout = 10,
++		.call_timeout = 60,
++		.udp_retrans_timeout = 10,
++		.tcp_retrans_timeout = 60,
++		.idle_timeout = 300,
++	},
++	.ic_dsa = {
++		.param_file = ISNS_ETCDIR "/dsa.params",
++	},
++};
++
++/*
++ * Default string values need to be dup'ed,
++ * so that later assignment does't try to free
++ * these strings.
++ */
++static inline void
++__isns_config_defaults(void)
++{
++	static int	defaults_init = 1;
++
++	if (!defaults_init)
++		return;
++
++#define DUP(member) \
++	if (isns_config.member) \
++		isns_config.member = isns_strdup(isns_config.member)
++
++	DUP(ic_source_name);
++	DUP(ic_database);
++	DUP(ic_server_name);
++	DUP(ic_bind_address);
++	DUP(ic_auth_key_file);
++	DUP(ic_server_key_file);
++	DUP(ic_client_keystore);
++	DUP(ic_control_socket);
++	DUP(ic_pidfile);
++	DUP(ic_control_name);
++	DUP(ic_control_key_file);
++	DUP(ic_local_registry_file);
++	DUP(ic_dsa.param_file);
++
++#undef DUP
++
++	defaults_init = 0;
++}
++
++/*
++ * Read the iSNS configuration file
++ */
++int
++isns_read_config(const char *filename)
++{
++	FILE	*fp;
++	char	*name, *pos;
++
++	__isns_config_defaults();
++
++	if ((fp = fopen(filename, "r")) == NULL) {
++		perror(filename);
++		return -1;
++	}
++
++	while ((pos = parser_get_next_line(fp)) != NULL) {
++		pos[strcspn(pos, "#")] = '\0';
++
++		if (!(name = parser_get_next_word(&pos)))
++			continue;
++
++		isns_config_set(name, pos);
++	}
++
++	fclose(fp);
++
++	/* Massage the config file */
++	if (isns_config.ic_security < 0) {
++		/* By default, we will enable authentication
++		 * whenever we find our private key, and
++		 * the server's public key. */
++		if (access(isns_config.ic_auth_key_file, R_OK) == 0
++		 && access(isns_config.ic_server_key_file, R_OK) == 0)
++			isns_config.ic_security = 1;
++		else
++			isns_config.ic_security = 0;
++	}
++
++	isns_init_names();
++
++	return 0;
++}
++
++int
++isns_config_set(const char *name, char *pos)
++{
++	char	*value;
++
++	value = parser_get_rest_of_line(&pos);
++	if (value)
++		while (isspace(*value) || *value == '=')
++			++value;
++	if (!strcasecmp(name, "HostName")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_host_name, value);
++	} else if (!strcasecmp(name, "SourceName")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_source_name, value);
++	} else if (!strcasecmp(name, "AuthName")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_auth_name, value);
++	} else if (!strcasecmp(name, "Database")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_database, value);
++	} else if (!strcasecmp(name, "ServerAddress")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_server_name, value);
++	} else if (!strcasecmp(name, "BindAddress")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_bind_address, value);
++	} else if (!strcasecmp(name, "ControlSocket")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_control_socket, value);
++	} else if (!strcasecmp(name, "PIDFile")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_pidfile, value);
++	} else if (!strcasecmp(name, "LocalRegistry")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_local_registry_file, value);
++	} else if (!strcasecmp(name, "RegistrationPeriod")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_registration_period = parse_timeout(value);
++	} else if (!strcasecmp(name, "SCNTimeout")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_scn_timeout = parse_timeout(value);
++	} else if (!strcasecmp(name, "SCNRetries")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_scn_retries = parse_int(value);
++	} else if (!strcasecmp(name, "SCNCallout")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_scn_callout, value);
++	} else if (!strcasecmp(name, "ESIMinInterval")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_esi_min_interval = parse_timeout(value);
++	} else if (!strcasecmp(name, "ESIMaxInterval")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_esi_max_interval = parse_timeout(value);
++	} else if (!strcasecmp(name, "ESIRetries")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_esi_retries = parse_int(value);
++	} else if (!strcasecmp(name, "DefaultDiscoveryDomain")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_use_default_domain = parse_int(value);
++	} else if (!strcasecmp(name, "SLPRegister")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_slp_register = parse_int(value);
++	} else if (!strcasecmp(name, "Security")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_security = parse_int(value);
++	} else if (!strcasecmp(name, "AuthKeyFile")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_auth_key_file, value);
++	} else if (!strcasecmp(name, "ServerKeyFile")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_server_key_file, value);
++	} else if (!strcasecmp(name, "ClientKeyStore")
++		|| !strcasecmp(name, "KeyStore")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_client_keystore, value);
++	} else if (!strcasecmp(name, "Control.SourceName")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_control_name, value);
++	} else if (!strcasecmp(name, "Control.AuthKeyFile")) {
++		if (!value)
++			goto no_value;
++		isns_assign_string(&isns_config.ic_control_key_file, value);
++	} else if (!strcasecmp(name, "Auth.ReplayWindow")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_auth.replay_window = parse_timeout(value);
++	} else if (!strcasecmp(name, "Auth.TimestampJitter")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_auth.timestamp_jitter = parse_timeout(value);
++	} else if (!strcasecmp(name, "Network.MaxSockets")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_network.max_sockets = parse_timeout(value);
++	} else if (!strcasecmp(name, "Network.ConnectTimeout")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_network.connect_timeout = parse_timeout(value);
++	} else if (!strcasecmp(name, "Network.ReconnectTimeout")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_network.reconnect_timeout = parse_timeout(value);
++	} else if (!strcasecmp(name, "Network.CallTimeout")) {
++		if (!value)
++			goto no_value;
++		isns_config.ic_network.call_timeout = parse_timeout(value);
++	} else {
++		fprintf(stderr, "Unknown config item %s=%s\n", name, value);
++	}
++	return 0;
++
++no_value:
++	fprintf(stderr,
++		"*** Missing value in configuration assignment for %s ***\n",
++		name);
++	return -1;
++}
+diff --git a/utils/open-isns/config.h.in b/utils/open-isns/config.h.in
+new file mode 100644
+index 0000000..b560bb0
+--- /dev/null
++++ b/utils/open-isns/config.h.in
+@@ -0,0 +1,103 @@
++/* config.h.in.  Generated from configure.ac by autoheader.  */
++
++/* Define if building universal (internal helper macro) */
++#undef AC_APPLE_UNIVERSAL_BUILD
++
++/* Define to 1 if you have the <errno.h> header file. */
++#undef HAVE_ERRNO_H
++
++/* Define to 1 if you have the <fcntl.h> header file. */
++#undef HAVE_FCNTL_H
++
++/* Define if you have the <getopt.h> header file. */
++#undef HAVE_GETOPT_H
++
++/* Define if you have the `getopt_long' function. */
++#undef HAVE_GETOPT_LONG
++
++/* Define to 1 if you have the <inttypes.h> header file. */
++#undef HAVE_INTTYPES_H
++
++/* Define to 1 if you have the <locale.h> header file. */
++#undef HAVE_LOCALE_H
++
++/* Define to 1 if you have the <malloc.h> header file. */
++#undef HAVE_MALLOC_H
++
++/* Define to 1 if you have the <memory.h> header file. */
++#undef HAVE_MEMORY_H
++
++/* Define to 1 if you have the <openssl/crypto.h> header file. */
++#undef HAVE_OPENSSL_CRYPTO_H
++
++/* Define to 1 if you have the <slp.h> header file. */
++#undef HAVE_SLP_H
++
++/* Define to 1 if you have the <stdint.h> header file. */
++#undef HAVE_STDINT_H
++
++/* Define to 1 if you have the <stdlib.h> header file. */
++#undef HAVE_STDLIB_H
++
++/* Define to 1 if you have the <strings.h> header file. */
++#undef HAVE_STRINGS_H
++
++/* Define to 1 if you have the <string.h> header file. */
++#undef HAVE_STRING_H
++
++/* Define to 1 if you have the <sys/stat.h> header file. */
++#undef HAVE_SYS_STAT_H
++
++/* Define to 1 if you have the <sys/time.h> header file. */
++#undef HAVE_SYS_TIME_H
++
++/* Define to 1 if you have the <sys/types.h> header file. */
++#undef HAVE_SYS_TYPES_H
++
++/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
++#undef HAVE_SYS_WAIT_H
++
++/* Define to 1 if you have the <unistd.h> header file. */
++#undef HAVE_UNISTD_H
++
++/* Define to the address where bug reports for this package should be sent. */
++#undef PACKAGE_BUGREPORT
++
++/* Define to the full name of this package. */
++#undef PACKAGE_NAME
++
++/* Define to the full name and version of this package. */
++#undef PACKAGE_STRING
++
++/* Define to the one symbol short name of this package. */
++#undef PACKAGE_TARNAME
++
++/* Define to the version of this package. */
++#undef PACKAGE_VERSION
++
++/* Define to 1 if you have the ANSI C header files. */
++#undef STDC_HEADERS
++
++/* Define if you want to support iSNS authentication */
++#undef WITH_SECURITY
++
++/* Define if you want to support SLP discovery */
++#undef WITH_SLP
++
++/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
++   significant byte first (like Motorola and SPARC, unlike Intel). */
++#if defined AC_APPLE_UNIVERSAL_BUILD
++# if defined __BIG_ENDIAN__
++#  define WORDS_BIGENDIAN 1
++# endif
++#else
++# ifndef WORDS_BIGENDIAN
++#  undef WORDS_BIGENDIAN
++# endif
++#endif
++
++/* Define to `__inline__' or `__inline' if that's what the C compiler
++   calls it, or to nothing if 'inline' is not supported under any name.  */
++#ifndef __cplusplus
++#undef inline
++#endif
+diff --git a/utils/open-isns/configure b/utils/open-isns/configure
+new file mode 100755
+index 0000000..2d1054b
+--- /dev/null
++++ b/utils/open-isns/configure
+@@ -0,0 +1,6727 @@
++#! /bin/sh
++# Guess values for system-dependent variables and create Makefiles.
++# Generated by GNU Autoconf 2.63 for open-isns 0.90.
++#
++# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
++# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
++# This configure script is free software; the Free Software Foundation
++# gives unlimited permission to copy, distribute and modify it.
++## --------------------- ##
++## M4sh Initialization.  ##
++## --------------------- ##
++
++# Be more Bourne compatible
++DUALCASE=1; export DUALCASE # for MKS sh
++if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
++  emulate sh
++  NULLCMD=:
++  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
++  # is contrary to our usage.  Disable this feature.
++  alias -g '${1+"$@"}'='"$@"'
++  setopt NO_GLOB_SUBST
++else
++  case `(set -o) 2>/dev/null` in
++  *posix*) set -o posix ;;
++esac
++
++fi
++
++
++
++
++# PATH needs CR
++# Avoid depending upon Character Ranges.
++as_cr_letters='abcdefghijklmnopqrstuvwxyz'
++as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
++as_cr_Letters=$as_cr_letters$as_cr_LETTERS
++as_cr_digits='0123456789'
++as_cr_alnum=$as_cr_Letters$as_cr_digits
++
++as_nl='
++'
++export as_nl
++# Printing a long string crashes Solaris 7 /usr/bin/printf.
++as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
++as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
++as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
++if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
++  as_echo='printf %s\n'
++  as_echo_n='printf %s'
++else
++  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
++    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
++    as_echo_n='/usr/ucb/echo -n'
++  else
++    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
++    as_echo_n_body='eval
++      arg=$1;
++      case $arg in
++      *"$as_nl"*)
++	expr "X$arg" : "X\\(.*\\)$as_nl";
++	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
++      esac;
++      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
++    '
++    export as_echo_n_body
++    as_echo_n='sh -c $as_echo_n_body as_echo'
++  fi
++  export as_echo_body
++  as_echo='sh -c $as_echo_body as_echo'
++fi
++
++# The user is always right.
++if test "${PATH_SEPARATOR+set}" != set; then
++  PATH_SEPARATOR=:
++  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
++    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
++      PATH_SEPARATOR=';'
++  }
++fi
++
++# Support unset when possible.
++if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
++  as_unset=unset
++else
++  as_unset=false
++fi
++
++
++# IFS
++# We need space, tab and new line, in precisely that order.  Quoting is
++# there to prevent editors from complaining about space-tab.
++# (If _AS_PATH_WALK were called with IFS unset, it would disable word
++# splitting by setting IFS to empty value.)
++IFS=" ""	$as_nl"
++
++# Find who we are.  Look in the path if we contain no directory separator.
++case $0 in
++  *[\\/]* ) as_myself=$0 ;;
++  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
++done
++IFS=$as_save_IFS
++
++     ;;
++esac
++# We did not find ourselves, most probably we were run as `sh COMMAND'
++# in which case we are not to be found in the path.
++if test "x$as_myself" = x; then
++  as_myself=$0
++fi
++if test ! -f "$as_myself"; then
++  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
++  { (exit 1); exit 1; }
++fi
++
++# Work around bugs in pre-3.0 UWIN ksh.
++for as_var in ENV MAIL MAILPATH
++do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
++done
++PS1='$ '
++PS2='> '
++PS4='+ '
++
++# NLS nuisances.
++LC_ALL=C
++export LC_ALL
++LANGUAGE=C
++export LANGUAGE
++
++# Required to use basename.
++if expr a : '\(a\)' >/dev/null 2>&1 &&
++   test "X`expr 00001 : '.*\(...\)'`" = X001; then
++  as_expr=expr
++else
++  as_expr=false
++fi
++
++if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
++  as_basename=basename
++else
++  as_basename=false
++fi
++
++
++# Name of the executable.
++as_me=`$as_basename -- "$0" ||
++$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
++	 X"$0" : 'X\(//\)$' \| \
++	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
++$as_echo X/"$0" |
++    sed '/^.*\/\([^/][^/]*\)\/*$/{
++	    s//\1/
++	    q
++	  }
++	  /^X\/\(\/\/\)$/{
++	    s//\1/
++	    q
++	  }
++	  /^X\/\(\/\).*/{
++	    s//\1/
++	    q
++	  }
++	  s/.*/./; q'`
++
++# CDPATH.
++$as_unset CDPATH
++
++
++if test "x$CONFIG_SHELL" = x; then
++  if (eval ":") 2>/dev/null; then
++  as_have_required=yes
++else
++  as_have_required=no
++fi
++
++  if test $as_have_required = yes &&	 (eval ":
++(as_func_return () {
++  (exit \$1)
++}
++as_func_success () {
++  as_func_return 0
++}
++as_func_failure () {
++  as_func_return 1
++}
++as_func_ret_success () {
++  return 0
++}
++as_func_ret_failure () {
++  return 1
++}
++
++exitcode=0
++if as_func_success; then
++  :
++else
++  exitcode=1
++  echo as_func_success failed.
++fi
++
++if as_func_failure; then
++  exitcode=1
++  echo as_func_failure succeeded.
++fi
++
++if as_func_ret_success; then
++  :
++else
++  exitcode=1
++  echo as_func_ret_success failed.
++fi
++
++if as_func_ret_failure; then
++  exitcode=1
++  echo as_func_ret_failure succeeded.
++fi
++
++if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
++  :
++else
++  exitcode=1
++  echo positional parameters were not saved.
++fi
++
++test \$exitcode = 0) || { (exit 1); exit 1; }
++
++(
++  as_lineno_1=\$LINENO
++  as_lineno_2=\$LINENO
++  test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
++  test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
++") 2> /dev/null; then
++  :
++else
++  as_candidate_shells=
++    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  case $as_dir in
++	 /*)
++	   for as_base in sh bash ksh sh5; do
++	     as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
++	   done;;
++       esac
++done
++IFS=$as_save_IFS
++
++
++      for as_shell in $as_candidate_shells $SHELL; do
++	 # Try only shells that exist, to save several forks.
++	 if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
++		{ ("$as_shell") 2> /dev/null <<\_ASEOF
++if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
++  emulate sh
++  NULLCMD=:
++  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
++  # is contrary to our usage.  Disable this feature.
++  alias -g '${1+"$@"}'='"$@"'
++  setopt NO_GLOB_SUBST
++else
++  case `(set -o) 2>/dev/null` in
++  *posix*) set -o posix ;;
++esac
++
++fi
++
++
++:
++_ASEOF
++}; then
++  CONFIG_SHELL=$as_shell
++	       as_have_required=yes
++	       if { "$as_shell" 2> /dev/null <<\_ASEOF
++if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
++  emulate sh
++  NULLCMD=:
++  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
++  # is contrary to our usage.  Disable this feature.
++  alias -g '${1+"$@"}'='"$@"'
++  setopt NO_GLOB_SUBST
++else
++  case `(set -o) 2>/dev/null` in
++  *posix*) set -o posix ;;
++esac
++
++fi
++
++
++:
++(as_func_return () {
++  (exit $1)
++}
++as_func_success () {
++  as_func_return 0
++}
++as_func_failure () {
++  as_func_return 1
++}
++as_func_ret_success () {
++  return 0
++}
++as_func_ret_failure () {
++  return 1
++}
++
++exitcode=0
++if as_func_success; then
++  :
++else
++  exitcode=1
++  echo as_func_success failed.
++fi
++
++if as_func_failure; then
++  exitcode=1
++  echo as_func_failure succeeded.
++fi
++
++if as_func_ret_success; then
++  :
++else
++  exitcode=1
++  echo as_func_ret_success failed.
++fi
++
++if as_func_ret_failure; then
++  exitcode=1
++  echo as_func_ret_failure succeeded.
++fi
++
++if ( set x; as_func_ret_success y && test x = "$1" ); then
++  :
++else
++  exitcode=1
++  echo positional parameters were not saved.
++fi
++
++test $exitcode = 0) || { (exit 1); exit 1; }
++
++(
++  as_lineno_1=$LINENO
++  as_lineno_2=$LINENO
++  test "x$as_lineno_1" != "x$as_lineno_2" &&
++  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
++
++_ASEOF
++}; then
++  break
++fi
++
++fi
++
++      done
++
++      if test "x$CONFIG_SHELL" != x; then
++  for as_var in BASH_ENV ENV
++	do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
++	done
++	export CONFIG_SHELL
++	exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
++fi
++
++
++    if test $as_have_required = no; then
++  echo This script requires a shell more modern than all the
++      echo shells that I found on your system.  Please install a
++      echo modern shell, or manually run the script under such a
++      echo shell if you do have one.
++      { (exit 1); exit 1; }
++fi
++
++
++fi
++
++fi
++
++
++
++(eval "as_func_return () {
++  (exit \$1)
++}
++as_func_success () {
++  as_func_return 0
++}
++as_func_failure () {
++  as_func_return 1
++}
++as_func_ret_success () {
++  return 0
++}
++as_func_ret_failure () {
++  return 1
++}
++
++exitcode=0
++if as_func_success; then
++  :
++else
++  exitcode=1
++  echo as_func_success failed.
++fi
++
++if as_func_failure; then
++  exitcode=1
++  echo as_func_failure succeeded.
++fi
++
++if as_func_ret_success; then
++  :
++else
++  exitcode=1
++  echo as_func_ret_success failed.
++fi
++
++if as_func_ret_failure; then
++  exitcode=1
++  echo as_func_ret_failure succeeded.
++fi
++
++if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
++  :
++else
++  exitcode=1
++  echo positional parameters were not saved.
++fi
++
++test \$exitcode = 0") || {
++  echo No shell found that supports shell functions.
++  echo Please tell bug-autoconf@gnu.org about your system,
++  echo including any error possibly output before this message.
++  echo This can help us improve future autoconf versions.
++  echo Configuration will now proceed without shell functions.
++}
++
++
++
++  as_lineno_1=$LINENO
++  as_lineno_2=$LINENO
++  test "x$as_lineno_1" != "x$as_lineno_2" &&
++  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
++
++  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
++  # uniformly replaced by the line number.  The first 'sed' inserts a
++  # line-number line after each line using $LINENO; the second 'sed'
++  # does the real work.  The second script uses 'N' to pair each
++  # line-number line with the line containing $LINENO, and appends
++  # trailing '-' during substitution so that $LINENO is not a special
++  # case at line end.
++  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
++  # scripts with optimization help from Paolo Bonzini.  Blame Lee
++  # E. McMahon (1931-1989) for sed's syntax.  :-)
++  sed -n '
++    p
++    /[$]LINENO/=
++  ' <$as_myself |
++    sed '
++      s/[$]LINENO.*/&-/
++      t lineno
++      b
++      :lineno
++      N
++      :loop
++      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
++      t loop
++      s/-\n.*//
++    ' >$as_me.lineno &&
++  chmod +x "$as_me.lineno" ||
++    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
++   { (exit 1); exit 1; }; }
++
++  # Don't try to exec as it changes $[0], causing all sort of problems
++  # (the dirname of $[0] is not the place where we might find the
++  # original and so on.  Autoconf is especially sensitive to this).
++  . "./$as_me.lineno"
++  # Exit status is that of the last command.
++  exit
++}
++
++
++if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
++  as_dirname=dirname
++else
++  as_dirname=false
++fi
++
++ECHO_C= ECHO_N= ECHO_T=
++case `echo -n x` in
++-n*)
++  case `echo 'x\c'` in
++  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
++  *)   ECHO_C='\c';;
++  esac;;
++*)
++  ECHO_N='-n';;
++esac
++if expr a : '\(a\)' >/dev/null 2>&1 &&
++   test "X`expr 00001 : '.*\(...\)'`" = X001; then
++  as_expr=expr
++else
++  as_expr=false
++fi
++
++rm -f conf$$ conf$$.exe conf$$.file
++if test -d conf$$.dir; then
++  rm -f conf$$.dir/conf$$.file
++else
++  rm -f conf$$.dir
++  mkdir conf$$.dir 2>/dev/null
++fi
++if (echo >conf$$.file) 2>/dev/null; then
++  if ln -s conf$$.file conf$$ 2>/dev/null; then
++    as_ln_s='ln -s'
++    # ... but there are two gotchas:
++    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
++    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
++    # In both cases, we have to default to `cp -p'.
++    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
++      as_ln_s='cp -p'
++  elif ln conf$$.file conf$$ 2>/dev/null; then
++    as_ln_s=ln
++  else
++    as_ln_s='cp -p'
++  fi
++else
++  as_ln_s='cp -p'
++fi
++rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
++rmdir conf$$.dir 2>/dev/null
++
++if mkdir -p . 2>/dev/null; then
++  as_mkdir_p=:
++else
++  test -d ./-p && rmdir ./-p
++  as_mkdir_p=false
++fi
++
++if test -x / >/dev/null 2>&1; then
++  as_test_x='test -x'
++else
++  if ls -dL / >/dev/null 2>&1; then
++    as_ls_L_option=L
++  else
++    as_ls_L_option=
++  fi
++  as_test_x='
++    eval sh -c '\''
++      if test -d "$1"; then
++	test -d "$1/.";
++      else
++	case $1 in
++	-*)set "./$1";;
++	esac;
++	case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
++	???[sx]*):;;*)false;;esac;fi
++    '\'' sh
++  '
++fi
++as_executable_p=$as_test_x
++
++# Sed expression to map a string onto a valid CPP name.
++as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
++
++# Sed expression to map a string onto a valid variable name.
++as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
++
++
++
++exec 7<&0 </dev/null 6>&1
++
++# Name of the host.
++# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
++# so uname gets run too.
++ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
++
++#
++# Initializations.
++#
++ac_default_prefix=/usr/local
++ac_clean_files=
++ac_config_libobj_dir=.
++LIBOBJS=
++cross_compiling=no
++subdirs=
++MFLAGS=
++MAKEFLAGS=
++SHELL=${CONFIG_SHELL-/bin/sh}
++
++# Identity of this package.
++PACKAGE_NAME='open-isns'
++PACKAGE_TARNAME='open-isns'
++PACKAGE_VERSION='0.90'
++PACKAGE_STRING='open-isns 0.90'
++PACKAGE_BUGREPORT=''
++
++ac_unique_file="isnsd.c"
++# Factoring default headers for most tests.
++ac_includes_default="\
++#include <stdio.h>
++#ifdef HAVE_SYS_TYPES_H
++# include <sys/types.h>
++#endif
++#ifdef HAVE_SYS_STAT_H
++# include <sys/stat.h>
++#endif
++#ifdef STDC_HEADERS
++# include <stdlib.h>
++# include <stddef.h>
++#else
++# ifdef HAVE_STDLIB_H
++#  include <stdlib.h>
++# endif
++#endif
++#ifdef HAVE_STRING_H
++# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
++#  include <memory.h>
++# endif
++# include <string.h>
++#endif
++#ifdef HAVE_STRINGS_H
++# include <strings.h>
++#endif
++#ifdef HAVE_INTTYPES_H
++# include <inttypes.h>
++#endif
++#ifdef HAVE_STDINT_H
++# include <stdint.h>
++#endif
++#ifdef HAVE_UNISTD_H
++# include <unistd.h>
++#endif"
++
++ac_subst_vars='LTLIBOBJS
++LIBOBJS
++OPTIMIZE
++SLPLIBS
++SECLIBS
++GETOPTSRC
++SH
++SET_MAKE
++LN_S
++INSTALL_DATA
++INSTALL_SCRIPT
++INSTALL_PROGRAM
++EGREP
++GREP
++CPP
++host_os
++host_vendor
++host_cpu
++host
++build_os
++build_vendor
++build_cpu
++build
++OBJEXT
++EXEEXT
++ac_ct_CC
++CPPFLAGS
++LDFLAGS
++CFLAGS
++CC
++target_alias
++host_alias
++build_alias
++LIBS
++ECHO_T
++ECHO_N
++ECHO_C
++DEFS
++mandir
++localedir
++libdir
++psdir
++pdfdir
++dvidir
++htmldir
++infodir
++docdir
++oldincludedir
++includedir
++localstatedir
++sharedstatedir
++sysconfdir
++datadir
++datarootdir
++libexecdir
++sbindir
++bindir
++program_transform_name
++prefix
++exec_prefix
++PACKAGE_BUGREPORT
++PACKAGE_STRING
++PACKAGE_VERSION
++PACKAGE_TARNAME
++PACKAGE_NAME
++PATH_SEPARATOR
++SHELL'
++ac_subst_files=''
++ac_user_opts='
++enable_option_checking
++with_security
++with_slp
++enable_memdebug
++'
++      ac_precious_vars='build_alias
++host_alias
++target_alias
++CC
++CFLAGS
++LDFLAGS
++LIBS
++CPPFLAGS
++CPP'
++
++
++# Initialize some variables set by options.
++ac_init_help=
++ac_init_version=false
++ac_unrecognized_opts=
++ac_unrecognized_sep=
++# The variables have the same names as the options, with
++# dashes changed to underlines.
++cache_file=/dev/null
++exec_prefix=NONE
++no_create=
++no_recursion=
++prefix=NONE
++program_prefix=NONE
++program_suffix=NONE
++program_transform_name=s,x,x,
++silent=
++site=
++srcdir=
++verbose=
++x_includes=NONE
++x_libraries=NONE
++
++# Installation directory options.
++# These are left unexpanded so users can "make install exec_prefix=/foo"
++# and all the variables that are supposed to be based on exec_prefix
++# by default will actually change.
++# Use braces instead of parens because sh, perl, etc. also accept them.
++# (The list follows the same order as the GNU Coding Standards.)
++bindir='${exec_prefix}/bin'
++sbindir='${exec_prefix}/sbin'
++libexecdir='${exec_prefix}/libexec'
++datarootdir='${prefix}/share'
++datadir='${datarootdir}'
++sysconfdir='${prefix}/etc'
++sharedstatedir='${prefix}/com'
++localstatedir='${prefix}/var'
++includedir='${prefix}/include'
++oldincludedir='/usr/include'
++docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
++infodir='${datarootdir}/info'
++htmldir='${docdir}'
++dvidir='${docdir}'
++pdfdir='${docdir}'
++psdir='${docdir}'
++libdir='${exec_prefix}/lib'
++localedir='${datarootdir}/locale'
++mandir='${datarootdir}/man'
++
++ac_prev=
++ac_dashdash=
++for ac_option
++do
++  # If the previous option needs an argument, assign it.
++  if test -n "$ac_prev"; then
++    eval $ac_prev=\$ac_option
++    ac_prev=
++    continue
++  fi
++
++  case $ac_option in
++  *=*)	ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
++  *)	ac_optarg=yes ;;
++  esac
++
++  # Accept the important Cygnus configure options, so we can diagnose typos.
++
++  case $ac_dashdash$ac_option in
++  --)
++    ac_dashdash=yes ;;
++
++  -bindir | --bindir | --bindi | --bind | --bin | --bi)
++    ac_prev=bindir ;;
++  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
++    bindir=$ac_optarg ;;
++
++  -build | --build | --buil | --bui | --bu)
++    ac_prev=build_alias ;;
++  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
++    build_alias=$ac_optarg ;;
++
++  -cache-file | --cache-file | --cache-fil | --cache-fi \
++  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
++    ac_prev=cache_file ;;
++  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
++  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
++    cache_file=$ac_optarg ;;
++
++  --config-cache | -C)
++    cache_file=config.cache ;;
++
++  -datadir | --datadir | --datadi | --datad)
++    ac_prev=datadir ;;
++  -datadir=* | --datadir=* | --datadi=* | --datad=*)
++    datadir=$ac_optarg ;;
++
++  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
++  | --dataroo | --dataro | --datar)
++    ac_prev=datarootdir ;;
++  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
++  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
++    datarootdir=$ac_optarg ;;
++
++  -disable-* | --disable-*)
++    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
++    # Reject names that are not valid shell variable names.
++    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
++      { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
++   { (exit 1); exit 1; }; }
++    ac_useropt_orig=$ac_useropt
++    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
++    case $ac_user_opts in
++      *"
++"enable_$ac_useropt"
++"*) ;;
++      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
++	 ac_unrecognized_sep=', ';;
++    esac
++    eval enable_$ac_useropt=no ;;
++
++  -docdir | --docdir | --docdi | --doc | --do)
++    ac_prev=docdir ;;
++  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
++    docdir=$ac_optarg ;;
++
++  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
++    ac_prev=dvidir ;;
++  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
++    dvidir=$ac_optarg ;;
++
++  -enable-* | --enable-*)
++    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
++    # Reject names that are not valid shell variable names.
++    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
++      { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
++   { (exit 1); exit 1; }; }
++    ac_useropt_orig=$ac_useropt
++    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
++    case $ac_user_opts in
++      *"
++"enable_$ac_useropt"
++"*) ;;
++      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
++	 ac_unrecognized_sep=', ';;
++    esac
++    eval enable_$ac_useropt=\$ac_optarg ;;
++
++  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
++  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
++  | --exec | --exe | --ex)
++    ac_prev=exec_prefix ;;
++  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
++  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
++  | --exec=* | --exe=* | --ex=*)
++    exec_prefix=$ac_optarg ;;
++
++  -gas | --gas | --ga | --g)
++    # Obsolete; use --with-gas.
++    with_gas=yes ;;
++
++  -help | --help | --hel | --he | -h)
++    ac_init_help=long ;;
++  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
++    ac_init_help=recursive ;;
++  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
++    ac_init_help=short ;;
++
++  -host | --host | --hos | --ho)
++    ac_prev=host_alias ;;
++  -host=* | --host=* | --hos=* | --ho=*)
++    host_alias=$ac_optarg ;;
++
++  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
++    ac_prev=htmldir ;;
++  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
++  | --ht=*)
++    htmldir=$ac_optarg ;;
++
++  -includedir | --includedir | --includedi | --included | --include \
++  | --includ | --inclu | --incl | --inc)
++    ac_prev=includedir ;;
++  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
++  | --includ=* | --inclu=* | --incl=* | --inc=*)
++    includedir=$ac_optarg ;;
++
++  -infodir | --infodir | --infodi | --infod | --info | --inf)
++    ac_prev=infodir ;;
++  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
++    infodir=$ac_optarg ;;
++
++  -libdir | --libdir | --libdi | --libd)
++    ac_prev=libdir ;;
++  -libdir=* | --libdir=* | --libdi=* | --libd=*)
++    libdir=$ac_optarg ;;
++
++  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
++  | --libexe | --libex | --libe)
++    ac_prev=libexecdir ;;
++  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
++  | --libexe=* | --libex=* | --libe=*)
++    libexecdir=$ac_optarg ;;
++
++  -localedir | --localedir | --localedi | --localed | --locale)
++    ac_prev=localedir ;;
++  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
++    localedir=$ac_optarg ;;
++
++  -localstatedir | --localstatedir | --localstatedi | --localstated \
++  | --localstate | --localstat | --localsta | --localst | --locals)
++    ac_prev=localstatedir ;;
++  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
++  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
++    localstatedir=$ac_optarg ;;
++
++  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
++    ac_prev=mandir ;;
++  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
++    mandir=$ac_optarg ;;
++
++  -nfp | --nfp | --nf)
++    # Obsolete; use --without-fp.
++    with_fp=no ;;
++
++  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
++  | --no-cr | --no-c | -n)
++    no_create=yes ;;
++
++  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
++  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
++    no_recursion=yes ;;
++
++  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
++  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
++  | --oldin | --oldi | --old | --ol | --o)
++    ac_prev=oldincludedir ;;
++  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
++  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
++  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
++    oldincludedir=$ac_optarg ;;
++
++  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
++    ac_prev=prefix ;;
++  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
++    prefix=$ac_optarg ;;
++
++  -program-prefix | --program-prefix | --program-prefi | --program-pref \
++  | --program-pre | --program-pr | --program-p)
++    ac_prev=program_prefix ;;
++  -program-prefix=* | --program-prefix=* | --program-prefi=* \
++  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
++    program_prefix=$ac_optarg ;;
++
++  -program-suffix | --program-suffix | --program-suffi | --program-suff \
++  | --program-suf | --program-su | --program-s)
++    ac_prev=program_suffix ;;
++  -program-suffix=* | --program-suffix=* | --program-suffi=* \
++  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
++    program_suffix=$ac_optarg ;;
++
++  -program-transform-name | --program-transform-name \
++  | --program-transform-nam | --program-transform-na \
++  | --program-transform-n | --program-transform- \
++  | --program-transform | --program-transfor \
++  | --program-transfo | --program-transf \
++  | --program-trans | --program-tran \
++  | --progr-tra | --program-tr | --program-t)
++    ac_prev=program_transform_name ;;
++  -program-transform-name=* | --program-transform-name=* \
++  | --program-transform-nam=* | --program-transform-na=* \
++  | --program-transform-n=* | --program-transform-=* \
++  | --program-transform=* | --program-transfor=* \
++  | --program-transfo=* | --program-transf=* \
++  | --program-trans=* | --program-tran=* \
++  | --progr-tra=* | --program-tr=* | --program-t=*)
++    program_transform_name=$ac_optarg ;;
++
++  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
++    ac_prev=pdfdir ;;
++  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
++    pdfdir=$ac_optarg ;;
++
++  -psdir | --psdir | --psdi | --psd | --ps)
++    ac_prev=psdir ;;
++  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
++    psdir=$ac_optarg ;;
++
++  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
++  | -silent | --silent | --silen | --sile | --sil)
++    silent=yes ;;
++
++  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
++    ac_prev=sbindir ;;
++  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
++  | --sbi=* | --sb=*)
++    sbindir=$ac_optarg ;;
++
++  -sharedstatedir | --sharedstatedir | --sharedstatedi \
++  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
++  | --sharedst | --shareds | --shared | --share | --shar \
++  | --sha | --sh)
++    ac_prev=sharedstatedir ;;
++  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
++  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
++  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
++  | --sha=* | --sh=*)
++    sharedstatedir=$ac_optarg ;;
++
++  -site | --site | --sit)
++    ac_prev=site ;;
++  -site=* | --site=* | --sit=*)
++    site=$ac_optarg ;;
++
++  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
++    ac_prev=srcdir ;;
++  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
++    srcdir=$ac_optarg ;;
++
++  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
++  | --syscon | --sysco | --sysc | --sys | --sy)
++    ac_prev=sysconfdir ;;
++  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
++  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
++    sysconfdir=$ac_optarg ;;
++
++  -target | --target | --targe | --targ | --tar | --ta | --t)
++    ac_prev=target_alias ;;
++  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
++    target_alias=$ac_optarg ;;
++
++  -v | -verbose | --verbose | --verbos | --verbo | --verb)
++    verbose=yes ;;
++
++  -version | --version | --versio | --versi | --vers | -V)
++    ac_init_version=: ;;
++
++  -with-* | --with-*)
++    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
++    # Reject names that are not valid shell variable names.
++    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
++      { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
++   { (exit 1); exit 1; }; }
++    ac_useropt_orig=$ac_useropt
++    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
++    case $ac_user_opts in
++      *"
++"with_$ac_useropt"
++"*) ;;
++      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
++	 ac_unrecognized_sep=', ';;
++    esac
++    eval with_$ac_useropt=\$ac_optarg ;;
++
++  -without-* | --without-*)
++    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
++    # Reject names that are not valid shell variable names.
++    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
++      { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
++   { (exit 1); exit 1; }; }
++    ac_useropt_orig=$ac_useropt
++    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
++    case $ac_user_opts in
++      *"
++"with_$ac_useropt"
++"*) ;;
++      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
++	 ac_unrecognized_sep=', ';;
++    esac
++    eval with_$ac_useropt=no ;;
++
++  --x)
++    # Obsolete; use --with-x.
++    with_x=yes ;;
++
++  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
++  | --x-incl | --x-inc | --x-in | --x-i)
++    ac_prev=x_includes ;;
++  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
++  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
++    x_includes=$ac_optarg ;;
++
++  -x-libraries | --x-libraries | --x-librarie | --x-librari \
++  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
++    ac_prev=x_libraries ;;
++  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
++  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
++    x_libraries=$ac_optarg ;;
++
++  -*) { $as_echo "$as_me: error: unrecognized option: $ac_option
++Try \`$0 --help' for more information." >&2
++   { (exit 1); exit 1; }; }
++    ;;
++
++  *=*)
++    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
++    # Reject names that are not valid shell variable names.
++    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
++      { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2
++   { (exit 1); exit 1; }; }
++    eval $ac_envvar=\$ac_optarg
++    export $ac_envvar ;;
++
++  *)
++    # FIXME: should be removed in autoconf 3.0.
++    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
++    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
++      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
++    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
++    ;;
++
++  esac
++done
++
++if test -n "$ac_prev"; then
++  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
++  { $as_echo "$as_me: error: missing argument to $ac_option" >&2
++   { (exit 1); exit 1; }; }
++fi
++
++if test -n "$ac_unrecognized_opts"; then
++  case $enable_option_checking in
++    no) ;;
++    fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2
++   { (exit 1); exit 1; }; } ;;
++    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
++  esac
++fi
++
++# Check all directory arguments for consistency.
++for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
++		datadir sysconfdir sharedstatedir localstatedir includedir \
++		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
++		libdir localedir mandir
++do
++  eval ac_val=\$$ac_var
++  # Remove trailing slashes.
++  case $ac_val in
++    */ )
++      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
++      eval $ac_var=\$ac_val;;
++  esac
++  # Be sure to have absolute directory names.
++  case $ac_val in
++    [\\/$]* | ?:[\\/]* )  continue;;
++    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
++  esac
++  { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
++   { (exit 1); exit 1; }; }
++done
++
++# There might be people who depend on the old broken behavior: `$host'
++# used to hold the argument of --host etc.
++# FIXME: To remove some day.
++build=$build_alias
++host=$host_alias
++target=$target_alias
++
++# FIXME: To remove some day.
++if test "x$host_alias" != x; then
++  if test "x$build_alias" = x; then
++    cross_compiling=maybe
++    $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
++    If a cross compiler is detected then cross compile mode will be used." >&2
++  elif test "x$build_alias" != "x$host_alias"; then
++    cross_compiling=yes
++  fi
++fi
++
++ac_tool_prefix=
++test -n "$host_alias" && ac_tool_prefix=$host_alias-
++
++test "$silent" = yes && exec 6>/dev/null
++
++
++ac_pwd=`pwd` && test -n "$ac_pwd" &&
++ac_ls_di=`ls -di .` &&
++ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
++  { $as_echo "$as_me: error: working directory cannot be determined" >&2
++   { (exit 1); exit 1; }; }
++test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
++  { $as_echo "$as_me: error: pwd does not report name of working directory" >&2
++   { (exit 1); exit 1; }; }
++
++
++# Find the source files, if location was not specified.
++if test -z "$srcdir"; then
++  ac_srcdir_defaulted=yes
++  # Try the directory containing this script, then the parent directory.
++  ac_confdir=`$as_dirname -- "$as_myself" ||
++$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
++	 X"$as_myself" : 'X\(//\)[^/]' \| \
++	 X"$as_myself" : 'X\(//\)$' \| \
++	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
++$as_echo X"$as_myself" |
++    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
++	    s//\1/
++	    q
++	  }
++	  /^X\(\/\/\)[^/].*/{
++	    s//\1/
++	    q
++	  }
++	  /^X\(\/\/\)$/{
++	    s//\1/
++	    q
++	  }
++	  /^X\(\/\).*/{
++	    s//\1/
++	    q
++	  }
++	  s/.*/./; q'`
++  srcdir=$ac_confdir
++  if test ! -r "$srcdir/$ac_unique_file"; then
++    srcdir=..
++  fi
++else
++  ac_srcdir_defaulted=no
++fi
++if test ! -r "$srcdir/$ac_unique_file"; then
++  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
++  { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
++   { (exit 1); exit 1; }; }
++fi
++ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
++ac_abs_confdir=`(
++	cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2
++   { (exit 1); exit 1; }; }
++	pwd)`
++# When building in place, set srcdir=.
++if test "$ac_abs_confdir" = "$ac_pwd"; then
++  srcdir=.
++fi
++# Remove unnecessary trailing slashes from srcdir.
++# Double slashes in file names in object file debugging info
++# mess up M-x gdb in Emacs.
++case $srcdir in
++*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
++esac
++for ac_var in $ac_precious_vars; do
++  eval ac_env_${ac_var}_set=\${${ac_var}+set}
++  eval ac_env_${ac_var}_value=\$${ac_var}
++  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
++  eval ac_cv_env_${ac_var}_value=\$${ac_var}
++done
++
++#
++# Report the --help message.
++#
++if test "$ac_init_help" = "long"; then
++  # Omit some internal or obsolete options to make the list less imposing.
++  # This message is too long to be a string in the A/UX 3.1 sh.
++  cat <<_ACEOF
++\`configure' configures open-isns 0.90 to adapt to many kinds of systems.
++
++Usage: $0 [OPTION]... [VAR=VALUE]...
++
++To assign environment variables (e.g., CC, CFLAGS...), specify them as
++VAR=VALUE.  See below for descriptions of some of the useful variables.
++
++Defaults for the options are specified in brackets.
++
++Configuration:
++  -h, --help              display this help and exit
++      --help=short        display options specific to this package
++      --help=recursive    display the short help of all the included packages
++  -V, --version           display version information and exit
++  -q, --quiet, --silent   do not print \`checking...' messages
++      --cache-file=FILE   cache test results in FILE [disabled]
++  -C, --config-cache      alias for \`--cache-file=config.cache'
++  -n, --no-create         do not create output files
++      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
++
++Installation directories:
++  --prefix=PREFIX         install architecture-independent files in PREFIX
++                          [$ac_default_prefix]
++  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
++                          [PREFIX]
++
++By default, \`make install' will install all the files in
++\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
++an installation prefix other than \`$ac_default_prefix' using \`--prefix',
++for instance \`--prefix=\$HOME'.
++
++For better control, use the options below.
++
++Fine tuning of the installation directories:
++  --bindir=DIR            user executables [EPREFIX/bin]
++  --sbindir=DIR           system admin executables [EPREFIX/sbin]
++  --libexecdir=DIR        program executables [EPREFIX/libexec]
++  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
++  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
++  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
++  --libdir=DIR            object code libraries [EPREFIX/lib]
++  --includedir=DIR        C header files [PREFIX/include]
++  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
++  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
++  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
++  --infodir=DIR           info documentation [DATAROOTDIR/info]
++  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
++  --mandir=DIR            man documentation [DATAROOTDIR/man]
++  --docdir=DIR            documentation root [DATAROOTDIR/doc/open-isns]
++  --htmldir=DIR           html documentation [DOCDIR]
++  --dvidir=DIR            dvi documentation [DOCDIR]
++  --pdfdir=DIR            pdf documentation [DOCDIR]
++  --psdir=DIR             ps documentation [DOCDIR]
++_ACEOF
++
++  cat <<\_ACEOF
++
++System types:
++  --build=BUILD     configure for building on BUILD [guessed]
++  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
++_ACEOF
++fi
++
++if test -n "$ac_init_help"; then
++  case $ac_init_help in
++     short | recursive ) echo "Configuration of open-isns 0.90:";;
++   esac
++  cat <<\_ACEOF
++
++Optional Features:
++  --disable-option-checking  ignore unrecognized --enable/--with options
++  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
++  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
++  --enable-memdebug       Enable malloc debugging
++
++Optional Packages:
++  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
++  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
++  --with-security         Enable iSNS authentication - requires OpenSSL
++  --with-slp              Enable SLP for server discovery - requires OpenSLP
++
++Some influential environment variables:
++  CC          C compiler command
++  CFLAGS      C compiler flags
++  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
++              nonstandard directory <lib dir>
++  LIBS        libraries to pass to the linker, e.g. -l<library>
++  CPPFLAGS    C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
++              you have headers in a nonstandard directory <include dir>
++  CPP         C preprocessor
++
++Use these variables to override the choices made by `configure' or to help
++it to find libraries and programs with nonstandard names/locations.
++
++_ACEOF
++ac_status=$?
++fi
++
++if test "$ac_init_help" = "recursive"; then
++  # If there are subdirs, report their specific --help.
++  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
++    test -d "$ac_dir" ||
++      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
++      continue
++    ac_builddir=.
++
++case "$ac_dir" in
++.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
++*)
++  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
++  # A ".." for each directory in $ac_dir_suffix.
++  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
++  case $ac_top_builddir_sub in
++  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
++  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
++  esac ;;
++esac
++ac_abs_top_builddir=$ac_pwd
++ac_abs_builddir=$ac_pwd$ac_dir_suffix
++# for backward compatibility:
++ac_top_builddir=$ac_top_build_prefix
++
++case $srcdir in
++  .)  # We are building in place.
++    ac_srcdir=.
++    ac_top_srcdir=$ac_top_builddir_sub
++    ac_abs_top_srcdir=$ac_pwd ;;
++  [\\/]* | ?:[\\/]* )  # Absolute name.
++    ac_srcdir=$srcdir$ac_dir_suffix;
++    ac_top_srcdir=$srcdir
++    ac_abs_top_srcdir=$srcdir ;;
++  *) # Relative name.
++    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
++    ac_top_srcdir=$ac_top_build_prefix$srcdir
++    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
++esac
++ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
++
++    cd "$ac_dir" || { ac_status=$?; continue; }
++    # Check for guested configure.
++    if test -f "$ac_srcdir/configure.gnu"; then
++      echo &&
++      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
++    elif test -f "$ac_srcdir/configure"; then
++      echo &&
++      $SHELL "$ac_srcdir/configure" --help=recursive
++    else
++      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
++    fi || ac_status=$?
++    cd "$ac_pwd" || { ac_status=$?; break; }
++  done
++fi
++
++test -n "$ac_init_help" && exit $ac_status
++if $ac_init_version; then
++  cat <<\_ACEOF
++open-isns configure 0.90
++generated by GNU Autoconf 2.63
++
++Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
++2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
++This configure script is free software; the Free Software Foundation
++gives unlimited permission to copy, distribute and modify it.
++_ACEOF
++  exit
++fi
++cat >config.log <<_ACEOF
++This file contains any messages produced by compilers while
++running configure, to aid debugging if configure makes a mistake.
++
++It was created by open-isns $as_me 0.90, which was
++generated by GNU Autoconf 2.63.  Invocation command line was
++
++  $ $0 $@
++
++_ACEOF
++exec 5>>config.log
++{
++cat <<_ASUNAME
++## --------- ##
++## Platform. ##
++## --------- ##
++
++hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
++uname -m = `(uname -m) 2>/dev/null || echo unknown`
++uname -r = `(uname -r) 2>/dev/null || echo unknown`
++uname -s = `(uname -s) 2>/dev/null || echo unknown`
++uname -v = `(uname -v) 2>/dev/null || echo unknown`
++
++/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
++/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
++
++/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
++/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
++/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
++/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
++/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
++/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
++/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
++
++_ASUNAME
++
++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  $as_echo "PATH: $as_dir"
++done
++IFS=$as_save_IFS
++
++} >&5
++
++cat >&5 <<_ACEOF
++
++
++## ----------- ##
++## Core tests. ##
++## ----------- ##
++
++_ACEOF
++
++
++# Keep a trace of the command line.
++# Strip out --no-create and --no-recursion so they do not pile up.
++# Strip out --silent because we don't want to record it for future runs.
++# Also quote any args containing shell meta-characters.
++# Make two passes to allow for proper duplicate-argument suppression.
++ac_configure_args=
++ac_configure_args0=
++ac_configure_args1=
++ac_must_keep_next=false
++for ac_pass in 1 2
++do
++  for ac_arg
++  do
++    case $ac_arg in
++    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
++    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
++    | -silent | --silent | --silen | --sile | --sil)
++      continue ;;
++    *\'*)
++      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
++    esac
++    case $ac_pass in
++    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
++    2)
++      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
++      if test $ac_must_keep_next = true; then
++	ac_must_keep_next=false # Got value, back to normal.
++      else
++	case $ac_arg in
++	  *=* | --config-cache | -C | -disable-* | --disable-* \
++	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
++	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
++	  | -with-* | --with-* | -without-* | --without-* | --x)
++	    case "$ac_configure_args0 " in
++	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
++	    esac
++	    ;;
++	  -* ) ac_must_keep_next=true ;;
++	esac
++      fi
++      ac_configure_args="$ac_configure_args '$ac_arg'"
++      ;;
++    esac
++  done
++done
++$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
++$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
++
++# When interrupted or exit'd, cleanup temporary files, and complete
++# config.log.  We remove comments because anyway the quotes in there
++# would cause problems or look ugly.
++# WARNING: Use '\'' to represent an apostrophe within the trap.
++# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
++trap 'exit_status=$?
++  # Save into config.log some information that might help in debugging.
++  {
++    echo
++
++    cat <<\_ASBOX
++## ---------------- ##
++## Cache variables. ##
++## ---------------- ##
++_ASBOX
++    echo
++    # The following way of writing the cache mishandles newlines in values,
++(
++  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
++    eval ac_val=\$$ac_var
++    case $ac_val in #(
++    *${as_nl}*)
++      case $ac_var in #(
++      *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
++$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
++      esac
++      case $ac_var in #(
++      _ | IFS | as_nl) ;; #(
++      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
++      *) $as_unset $ac_var ;;
++      esac ;;
++    esac
++  done
++  (set) 2>&1 |
++    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
++    *${as_nl}ac_space=\ *)
++      sed -n \
++	"s/'\''/'\''\\\\'\'''\''/g;
++	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
++      ;; #(
++    *)
++      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
++      ;;
++    esac |
++    sort
++)
++    echo
++
++    cat <<\_ASBOX
++## ----------------- ##
++## Output variables. ##
++## ----------------- ##
++_ASBOX
++    echo
++    for ac_var in $ac_subst_vars
++    do
++      eval ac_val=\$$ac_var
++      case $ac_val in
++      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
++      esac
++      $as_echo "$ac_var='\''$ac_val'\''"
++    done | sort
++    echo
++
++    if test -n "$ac_subst_files"; then
++      cat <<\_ASBOX
++## ------------------- ##
++## File substitutions. ##
++## ------------------- ##
++_ASBOX
++      echo
++      for ac_var in $ac_subst_files
++      do
++	eval ac_val=\$$ac_var
++	case $ac_val in
++	*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
++	esac
++	$as_echo "$ac_var='\''$ac_val'\''"
++      done | sort
++      echo
++    fi
++
++    if test -s confdefs.h; then
++      cat <<\_ASBOX
++## ----------- ##
++## confdefs.h. ##
++## ----------- ##
++_ASBOX
++      echo
++      cat confdefs.h
++      echo
++    fi
++    test "$ac_signal" != 0 &&
++      $as_echo "$as_me: caught signal $ac_signal"
++    $as_echo "$as_me: exit $exit_status"
++  } >&5
++  rm -f core *.core core.conftest.* &&
++    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
++    exit $exit_status
++' 0
++for ac_signal in 1 2 13 15; do
++  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
++done
++ac_signal=0
++
++# confdefs.h avoids OS command line length limits that DEFS can exceed.
++rm -f -r conftest* confdefs.h
++
++# Predefined preprocessor variables.
++
++cat >>confdefs.h <<_ACEOF
++#define PACKAGE_NAME "$PACKAGE_NAME"
++_ACEOF
++
++
++cat >>confdefs.h <<_ACEOF
++#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
++_ACEOF
++
++
++cat >>confdefs.h <<_ACEOF
++#define PACKAGE_VERSION "$PACKAGE_VERSION"
++_ACEOF
++
++
++cat >>confdefs.h <<_ACEOF
++#define PACKAGE_STRING "$PACKAGE_STRING"
++_ACEOF
++
++
++cat >>confdefs.h <<_ACEOF
++#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
++_ACEOF
++
++
++# Let the site file select an alternate cache file if it wants to.
++# Prefer an explicitly selected file to automatically selected ones.
++ac_site_file1=NONE
++ac_site_file2=NONE
++if test -n "$CONFIG_SITE"; then
++  ac_site_file1=$CONFIG_SITE
++elif test "x$prefix" != xNONE; then
++  ac_site_file1=$prefix/share/config.site
++  ac_site_file2=$prefix/etc/config.site
++else
++  ac_site_file1=$ac_default_prefix/share/config.site
++  ac_site_file2=$ac_default_prefix/etc/config.site
++fi
++for ac_site_file in "$ac_site_file1" "$ac_site_file2"
++do
++  test "x$ac_site_file" = xNONE && continue
++  if test -r "$ac_site_file"; then
++    { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
++$as_echo "$as_me: loading site script $ac_site_file" >&6;}
++    sed 's/^/| /' "$ac_site_file" >&5
++    . "$ac_site_file"
++  fi
++done
++
++if test -r "$cache_file"; then
++  # Some versions of bash will fail to source /dev/null (special
++  # files actually), so we avoid doing that.
++  if test -f "$cache_file"; then
++    { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5
++$as_echo "$as_me: loading cache $cache_file" >&6;}
++    case $cache_file in
++      [\\/]* | ?:[\\/]* ) . "$cache_file";;
++      *)                      . "./$cache_file";;
++    esac
++  fi
++else
++  { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5
++$as_echo "$as_me: creating cache $cache_file" >&6;}
++  >$cache_file
++fi
++
++# Check that the precious variables saved in the cache have kept the same
++# value.
++ac_cache_corrupted=false
++for ac_var in $ac_precious_vars; do
++  eval ac_old_set=\$ac_cv_env_${ac_var}_set
++  eval ac_new_set=\$ac_env_${ac_var}_set
++  eval ac_old_val=\$ac_cv_env_${ac_var}_value
++  eval ac_new_val=\$ac_env_${ac_var}_value
++  case $ac_old_set,$ac_new_set in
++    set,)
++      { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
++$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
++      ac_cache_corrupted=: ;;
++    ,set)
++      { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
++$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
++      ac_cache_corrupted=: ;;
++    ,);;
++    *)
++      if test "x$ac_old_val" != "x$ac_new_val"; then
++	# differences in whitespace do not lead to failure.
++	ac_old_val_w=`echo x $ac_old_val`
++	ac_new_val_w=`echo x $ac_new_val`
++	if test "$ac_old_val_w" != "$ac_new_val_w"; then
++	  { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
++$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
++	  ac_cache_corrupted=:
++	else
++	  { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
++$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
++	  eval $ac_var=\$ac_old_val
++	fi
++	{ $as_echo "$as_me:$LINENO:   former value:  \`$ac_old_val'" >&5
++$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
++	{ $as_echo "$as_me:$LINENO:   current value: \`$ac_new_val'" >&5
++$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
++      fi;;
++  esac
++  # Pass precious variables to config.status.
++  if test "$ac_new_set" = set; then
++    case $ac_new_val in
++    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
++    *) ac_arg=$ac_var=$ac_new_val ;;
++    esac
++    case " $ac_configure_args " in
++      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
++      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
++    esac
++  fi
++done
++if $ac_cache_corrupted; then
++  { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
++  { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
++$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
++  { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
++$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
++   { (exit 1); exit 1; }; }
++fi
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++ac_ext=c
++ac_cpp='$CPP $CPPFLAGS'
++ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
++ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
++ac_compiler_gnu=$ac_cv_c_compiler_gnu
++
++
++
++ac_aux_dir=
++for ac_dir in aclocal "$srcdir"/aclocal; do
++  if test -f "$ac_dir/install-sh"; then
++    ac_aux_dir=$ac_dir
++    ac_install_sh="$ac_aux_dir/install-sh -c"
++    break
++  elif test -f "$ac_dir/install.sh"; then
++    ac_aux_dir=$ac_dir
++    ac_install_sh="$ac_aux_dir/install.sh -c"
++    break
++  elif test -f "$ac_dir/shtool"; then
++    ac_aux_dir=$ac_dir
++    ac_install_sh="$ac_aux_dir/shtool install -c"
++    break
++  fi
++done
++if test -z "$ac_aux_dir"; then
++  { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in aclocal \"$srcdir\"/aclocal" >&5
++$as_echo "$as_me: error: cannot find install-sh or install.sh in aclocal \"$srcdir\"/aclocal" >&2;}
++   { (exit 1); exit 1; }; }
++fi
++
++# These three variables are undocumented and unsupported,
++# and are intended to be withdrawn in a future Autoconf release.
++# They can cause serious problems if a builder's source tree is in a directory
++# whose full name contains unusual characters.
++ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
++ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
++ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
++
++
++
++ac_config_headers="$ac_config_headers config.h"
++
++
++ac_ext=c
++ac_cpp='$CPP $CPPFLAGS'
++ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
++ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
++ac_compiler_gnu=$ac_cv_c_compiler_gnu
++if test -n "$ac_tool_prefix"; then
++  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
++set dummy ${ac_tool_prefix}gcc; ac_word=$2
++{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
++$as_echo_n "checking for $ac_word... " >&6; }
++if test "${ac_cv_prog_CC+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  if test -n "$CC"; then
++  ac_cv_prog_CC="$CC" # Let the user override the test.
++else
++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  for ac_exec_ext in '' $ac_executable_extensions; do
++  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++    ac_cv_prog_CC="${ac_tool_prefix}gcc"
++    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
++    break 2
++  fi
++done
++done
++IFS=$as_save_IFS
++
++fi
++fi
++CC=$ac_cv_prog_CC
++if test -n "$CC"; then
++  { $as_echo "$as_me:$LINENO: result: $CC" >&5
++$as_echo "$CC" >&6; }
++else
++  { $as_echo "$as_me:$LINENO: result: no" >&5
++$as_echo "no" >&6; }
++fi
++
++
++fi
++if test -z "$ac_cv_prog_CC"; then
++  ac_ct_CC=$CC
++  # Extract the first word of "gcc", so it can be a program name with args.
++set dummy gcc; ac_word=$2
++{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
++$as_echo_n "checking for $ac_word... " >&6; }
++if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  if test -n "$ac_ct_CC"; then
++  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
++else
++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  for ac_exec_ext in '' $ac_executable_extensions; do
++  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++    ac_cv_prog_ac_ct_CC="gcc"
++    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
++    break 2
++  fi
++done
++done
++IFS=$as_save_IFS
++
++fi
++fi
++ac_ct_CC=$ac_cv_prog_ac_ct_CC
++if test -n "$ac_ct_CC"; then
++  { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
++$as_echo "$ac_ct_CC" >&6; }
++else
++  { $as_echo "$as_me:$LINENO: result: no" >&5
++$as_echo "no" >&6; }
++fi
++
++  if test "x$ac_ct_CC" = x; then
++    CC=""
++  else
++    case $cross_compiling:$ac_tool_warned in
++yes:)
++{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
++$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
++ac_tool_warned=yes ;;
++esac
++    CC=$ac_ct_CC
++  fi
++else
++  CC="$ac_cv_prog_CC"
++fi
++
++if test -z "$CC"; then
++          if test -n "$ac_tool_prefix"; then
++    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
++set dummy ${ac_tool_prefix}cc; ac_word=$2
++{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
++$as_echo_n "checking for $ac_word... " >&6; }
++if test "${ac_cv_prog_CC+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  if test -n "$CC"; then
++  ac_cv_prog_CC="$CC" # Let the user override the test.
++else
++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  for ac_exec_ext in '' $ac_executable_extensions; do
++  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++    ac_cv_prog_CC="${ac_tool_prefix}cc"
++    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
++    break 2
++  fi
++done
++done
++IFS=$as_save_IFS
++
++fi
++fi
++CC=$ac_cv_prog_CC
++if test -n "$CC"; then
++  { $as_echo "$as_me:$LINENO: result: $CC" >&5
++$as_echo "$CC" >&6; }
++else
++  { $as_echo "$as_me:$LINENO: result: no" >&5
++$as_echo "no" >&6; }
++fi
++
++
++  fi
++fi
++if test -z "$CC"; then
++  # Extract the first word of "cc", so it can be a program name with args.
++set dummy cc; ac_word=$2
++{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
++$as_echo_n "checking for $ac_word... " >&6; }
++if test "${ac_cv_prog_CC+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  if test -n "$CC"; then
++  ac_cv_prog_CC="$CC" # Let the user override the test.
++else
++  ac_prog_rejected=no
++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  for ac_exec_ext in '' $ac_executable_extensions; do
++  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
++       ac_prog_rejected=yes
++       continue
++     fi
++    ac_cv_prog_CC="cc"
++    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
++    break 2
++  fi
++done
++done
++IFS=$as_save_IFS
++
++if test $ac_prog_rejected = yes; then
++  # We found a bogon in the path, so make sure we never use it.
++  set dummy $ac_cv_prog_CC
++  shift
++  if test $# != 0; then
++    # We chose a different compiler from the bogus one.
++    # However, it has the same basename, so the bogon will be chosen
++    # first if we set CC to just the basename; use the full file name.
++    shift
++    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
++  fi
++fi
++fi
++fi
++CC=$ac_cv_prog_CC
++if test -n "$CC"; then
++  { $as_echo "$as_me:$LINENO: result: $CC" >&5
++$as_echo "$CC" >&6; }
++else
++  { $as_echo "$as_me:$LINENO: result: no" >&5
++$as_echo "no" >&6; }
++fi
++
++
++fi
++if test -z "$CC"; then
++  if test -n "$ac_tool_prefix"; then
++  for ac_prog in cl.exe
++  do
++    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
++set dummy $ac_tool_prefix$ac_prog; ac_word=$2
++{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
++$as_echo_n "checking for $ac_word... " >&6; }
++if test "${ac_cv_prog_CC+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  if test -n "$CC"; then
++  ac_cv_prog_CC="$CC" # Let the user override the test.
++else
++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  for ac_exec_ext in '' $ac_executable_extensions; do
++  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
++    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
++    break 2
++  fi
++done
++done
++IFS=$as_save_IFS
++
++fi
++fi
++CC=$ac_cv_prog_CC
++if test -n "$CC"; then
++  { $as_echo "$as_me:$LINENO: result: $CC" >&5
++$as_echo "$CC" >&6; }
++else
++  { $as_echo "$as_me:$LINENO: result: no" >&5
++$as_echo "no" >&6; }
++fi
++
++
++    test -n "$CC" && break
++  done
++fi
++if test -z "$CC"; then
++  ac_ct_CC=$CC
++  for ac_prog in cl.exe
++do
++  # Extract the first word of "$ac_prog", so it can be a program name with args.
++set dummy $ac_prog; ac_word=$2
++{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
++$as_echo_n "checking for $ac_word... " >&6; }
++if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  if test -n "$ac_ct_CC"; then
++  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
++else
++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  for ac_exec_ext in '' $ac_executable_extensions; do
++  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++    ac_cv_prog_ac_ct_CC="$ac_prog"
++    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
++    break 2
++  fi
++done
++done
++IFS=$as_save_IFS
++
++fi
++fi
++ac_ct_CC=$ac_cv_prog_ac_ct_CC
++if test -n "$ac_ct_CC"; then
++  { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
++$as_echo "$ac_ct_CC" >&6; }
++else
++  { $as_echo "$as_me:$LINENO: result: no" >&5
++$as_echo "no" >&6; }
++fi
++
++
++  test -n "$ac_ct_CC" && break
++done
++
++  if test "x$ac_ct_CC" = x; then
++    CC=""
++  else
++    case $cross_compiling:$ac_tool_warned in
++yes:)
++{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
++$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
++ac_tool_warned=yes ;;
++esac
++    CC=$ac_ct_CC
++  fi
++fi
++
++fi
++
++
++test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
++{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
++See \`config.log' for more details." >&5
++$as_echo "$as_me: error: no acceptable C compiler found in \$PATH
++See \`config.log' for more details." >&2;}
++   { (exit 1); exit 1; }; }; }
++
++# Provide some information about the compiler.
++$as_echo "$as_me:$LINENO: checking for C compiler version" >&5
++set X $ac_compile
++ac_compiler=$2
++{ (ac_try="$ac_compiler --version >&5"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compiler --version >&5") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }
++{ (ac_try="$ac_compiler -v >&5"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compiler -v >&5") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }
++{ (ac_try="$ac_compiler -V >&5"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compiler -V >&5") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }
++
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++
++int
++main ()
++{
++
++  ;
++  return 0;
++}
++_ACEOF
++ac_clean_files_save=$ac_clean_files
++ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
++# Try to create an executable without -o first, disregard a.out.
++# It will help us diagnose broken compilers, and finding out an intuition
++# of exeext.
++{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
++$as_echo_n "checking for C compiler default output file name... " >&6; }
++ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
++
++# The possible output files:
++ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
++
++ac_rmfiles=
++for ac_file in $ac_files
++do
++  case $ac_file in
++    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
++    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
++  esac
++done
++rm -f $ac_rmfiles
++
++if { (ac_try="$ac_link_default"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_link_default") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; then
++  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
++# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
++# in a Makefile.  We should not override ac_cv_exeext if it was cached,
++# so that the user can short-circuit this test for compilers unknown to
++# Autoconf.
++for ac_file in $ac_files ''
++do
++  test -f "$ac_file" || continue
++  case $ac_file in
++    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
++	;;
++    [ab].out )
++	# We found the default executable, but exeext='' is most
++	# certainly right.
++	break;;
++    *.* )
++        if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
++	then :; else
++	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
++	fi
++	# We set ac_cv_exeext here because the later test for it is not
++	# safe: cross compilers may not add the suffix if given an `-o'
++	# argument, so we may need to know it at that point already.
++	# Even if this section looks crufty: it has the advantage of
++	# actually working.
++	break;;
++    * )
++	break;;
++  esac
++done
++test "$ac_cv_exeext" = no && ac_cv_exeext=
++
++else
++  ac_file=''
++fi
++
++{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5
++$as_echo "$ac_file" >&6; }
++if test -z "$ac_file"; then
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
++{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables
++See \`config.log' for more details." >&5
++$as_echo "$as_me: error: C compiler cannot create executables
++See \`config.log' for more details." >&2;}
++   { (exit 77); exit 77; }; }; }
++fi
++
++ac_exeext=$ac_cv_exeext
++
++# Check that the compiler produces executables we can run.  If not, either
++# the compiler is broken, or we cross compile.
++{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5
++$as_echo_n "checking whether the C compiler works... " >&6; }
++# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
++# If not cross compiling, check that we can run a simple program.
++if test "$cross_compiling" != yes; then
++  if { ac_try='./$ac_file'
++  { (case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_try") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; }; then
++    cross_compiling=no
++  else
++    if test "$cross_compiling" = maybe; then
++	cross_compiling=yes
++    else
++	{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
++{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs.
++If you meant to cross compile, use \`--host'.
++See \`config.log' for more details." >&5
++$as_echo "$as_me: error: cannot run C compiled programs.
++If you meant to cross compile, use \`--host'.
++See \`config.log' for more details." >&2;}
++   { (exit 1); exit 1; }; }; }
++    fi
++  fi
++fi
++{ $as_echo "$as_me:$LINENO: result: yes" >&5
++$as_echo "yes" >&6; }
++
++rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
++ac_clean_files=$ac_clean_files_save
++# Check that the compiler produces executables we can run.  If not, either
++# the compiler is broken, or we cross compile.
++{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
++$as_echo_n "checking whether we are cross compiling... " >&6; }
++{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5
++$as_echo "$cross_compiling" >&6; }
++
++{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5
++$as_echo_n "checking for suffix of executables... " >&6; }
++if { (ac_try="$ac_link"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_link") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; then
++  # If both `conftest.exe' and `conftest' are `present' (well, observable)
++# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
++# work properly (i.e., refer to `conftest.exe'), while it won't with
++# `rm'.
++for ac_file in conftest.exe conftest conftest.*; do
++  test -f "$ac_file" || continue
++  case $ac_file in
++    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
++    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
++	  break;;
++    * ) break;;
++  esac
++done
++else
++  { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
++{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
++See \`config.log' for more details." >&5
++$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
++See \`config.log' for more details." >&2;}
++   { (exit 1); exit 1; }; }; }
++fi
++
++rm -f conftest$ac_cv_exeext
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
++$as_echo "$ac_cv_exeext" >&6; }
++
++rm -f conftest.$ac_ext
++EXEEXT=$ac_cv_exeext
++ac_exeext=$EXEEXT
++{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5
++$as_echo_n "checking for suffix of object files... " >&6; }
++if test "${ac_cv_objext+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++
++int
++main ()
++{
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.o conftest.obj
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; then
++  for ac_file in conftest.o conftest.obj conftest.*; do
++  test -f "$ac_file" || continue;
++  case $ac_file in
++    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
++    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
++       break;;
++  esac
++done
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
++{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
++See \`config.log' for more details." >&5
++$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile
++See \`config.log' for more details." >&2;}
++   { (exit 1); exit 1; }; }; }
++fi
++
++rm -f conftest.$ac_cv_objext conftest.$ac_ext
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
++$as_echo "$ac_cv_objext" >&6; }
++OBJEXT=$ac_cv_objext
++ac_objext=$OBJEXT
++{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
++$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
++if test "${ac_cv_c_compiler_gnu+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++
++int
++main ()
++{
++#ifndef __GNUC__
++       choke me
++#endif
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_compiler_gnu=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_compiler_gnu=no
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++ac_cv_c_compiler_gnu=$ac_compiler_gnu
++
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
++$as_echo "$ac_cv_c_compiler_gnu" >&6; }
++if test $ac_compiler_gnu = yes; then
++  GCC=yes
++else
++  GCC=
++fi
++ac_test_CFLAGS=${CFLAGS+set}
++ac_save_CFLAGS=$CFLAGS
++{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
++$as_echo_n "checking whether $CC accepts -g... " >&6; }
++if test "${ac_cv_prog_cc_g+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  ac_save_c_werror_flag=$ac_c_werror_flag
++   ac_c_werror_flag=yes
++   ac_cv_prog_cc_g=no
++   CFLAGS="-g"
++   cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++
++int
++main ()
++{
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_cv_prog_cc_g=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	CFLAGS=""
++      cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++
++int
++main ()
++{
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  :
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_c_werror_flag=$ac_save_c_werror_flag
++	 CFLAGS="-g"
++	 cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++
++int
++main ()
++{
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_cv_prog_cc_g=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++   ac_c_werror_flag=$ac_save_c_werror_flag
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
++$as_echo "$ac_cv_prog_cc_g" >&6; }
++if test "$ac_test_CFLAGS" = set; then
++  CFLAGS=$ac_save_CFLAGS
++elif test $ac_cv_prog_cc_g = yes; then
++  if test "$GCC" = yes; then
++    CFLAGS="-g -O2"
++  else
++    CFLAGS="-g"
++  fi
++else
++  if test "$GCC" = yes; then
++    CFLAGS="-O2"
++  else
++    CFLAGS=
++  fi
++fi
++{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
++$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
++if test "${ac_cv_prog_cc_c89+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  ac_cv_prog_cc_c89=no
++ac_save_CC=$CC
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <stdarg.h>
++#include <stdio.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
++struct buf { int x; };
++FILE * (*rcsopen) (struct buf *, struct stat *, int);
++static char *e (p, i)
++     char **p;
++     int i;
++{
++  return p[i];
++}
++static char *f (char * (*g) (char **, int), char **p, ...)
++{
++  char *s;
++  va_list v;
++  va_start (v,p);
++  s = g (p, va_arg (v,int));
++  va_end (v);
++  return s;
++}
++
++/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
++   function prototypes and stuff, but not '\xHH' hex character constants.
++   These don't provoke an error unfortunately, instead are silently treated
++   as 'x'.  The following induces an error, until -std is added to get
++   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
++   array size at least.  It's necessary to write '\x00'==0 to get something
++   that's true only with -std.  */
++int osf4_cc_array ['\x00' == 0 ? 1 : -1];
++
++/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
++   inside strings and character constants.  */
++#define FOO(x) 'x'
++int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
++
++int test (int i, double x);
++struct s1 {int (*f) (int a);};
++struct s2 {int (*f) (double a);};
++int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
++int argc;
++char **argv;
++int
++main ()
++{
++return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
++  ;
++  return 0;
++}
++_ACEOF
++for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
++	-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
++do
++  CC="$ac_save_CC $ac_arg"
++  rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_cv_prog_cc_c89=$ac_arg
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++
++fi
++
++rm -f core conftest.err conftest.$ac_objext
++  test "x$ac_cv_prog_cc_c89" != "xno" && break
++done
++rm -f conftest.$ac_ext
++CC=$ac_save_CC
++
++fi
++# AC_CACHE_VAL
++case "x$ac_cv_prog_cc_c89" in
++  x)
++    { $as_echo "$as_me:$LINENO: result: none needed" >&5
++$as_echo "none needed" >&6; } ;;
++  xno)
++    { $as_echo "$as_me:$LINENO: result: unsupported" >&5
++$as_echo "unsupported" >&6; } ;;
++  *)
++    CC="$CC $ac_cv_prog_cc_c89"
++    { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
++$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
++esac
++
++
++ac_ext=c
++ac_cpp='$CPP $CPPFLAGS'
++ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
++ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
++ac_compiler_gnu=$ac_cv_c_compiler_gnu
++
++# Make sure we can run config.sub.
++$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
++  { { $as_echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5
++$as_echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;}
++   { (exit 1); exit 1; }; }
++
++{ $as_echo "$as_me:$LINENO: checking build system type" >&5
++$as_echo_n "checking build system type... " >&6; }
++if test "${ac_cv_build+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  ac_build_alias=$build_alias
++test "x$ac_build_alias" = x &&
++  ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
++test "x$ac_build_alias" = x &&
++  { { $as_echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
++$as_echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
++   { (exit 1); exit 1; }; }
++ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
++  { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5
++$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;}
++   { (exit 1); exit 1; }; }
++
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_build" >&5
++$as_echo "$ac_cv_build" >&6; }
++case $ac_cv_build in
++*-*-*) ;;
++*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical build" >&5
++$as_echo "$as_me: error: invalid value of canonical build" >&2;}
++   { (exit 1); exit 1; }; };;
++esac
++build=$ac_cv_build
++ac_save_IFS=$IFS; IFS='-'
++set x $ac_cv_build
++shift
++build_cpu=$1
++build_vendor=$2
++shift; shift
++# Remember, the first character of IFS is used to create $*,
++# except with old shells:
++build_os=$*
++IFS=$ac_save_IFS
++case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
++
++
++{ $as_echo "$as_me:$LINENO: checking host system type" >&5
++$as_echo_n "checking host system type... " >&6; }
++if test "${ac_cv_host+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  if test "x$host_alias" = x; then
++  ac_cv_host=$ac_cv_build
++else
++  ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
++    { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5
++$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;}
++   { (exit 1); exit 1; }; }
++fi
++
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_host" >&5
++$as_echo "$ac_cv_host" >&6; }
++case $ac_cv_host in
++*-*-*) ;;
++*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical host" >&5
++$as_echo "$as_me: error: invalid value of canonical host" >&2;}
++   { (exit 1); exit 1; }; };;
++esac
++host=$ac_cv_host
++ac_save_IFS=$IFS; IFS='-'
++set x $ac_cv_host
++shift
++host_cpu=$1
++host_vendor=$2
++shift; shift
++# Remember, the first character of IFS is used to create $*,
++# except with old shells:
++host_os=$*
++IFS=$ac_save_IFS
++case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
++
++
++
++ac_ext=c
++ac_cpp='$CPP $CPPFLAGS'
++ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
++ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
++ac_compiler_gnu=$ac_cv_c_compiler_gnu
++{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
++$as_echo_n "checking how to run the C preprocessor... " >&6; }
++# On Suns, sometimes $CPP names a directory.
++if test -n "$CPP" && test -d "$CPP"; then
++  CPP=
++fi
++if test -z "$CPP"; then
++  if test "${ac_cv_prog_CPP+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++      # Double quotes because CPP needs to be expanded
++    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
++    do
++      ac_preproc_ok=false
++for ac_c_preproc_warn_flag in '' yes
++do
++  # Use a header file that comes with gcc, so configuring glibc
++  # with a fresh cross-compiler works.
++  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
++  # <limits.h> exists even on freestanding compilers.
++  # On the NeXT, cc -E runs the code through the compiler's parser,
++  # not just through cpp. "Syntax error" is here to catch this case.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#ifdef __STDC__
++# include <limits.h>
++#else
++# include <assert.h>
++#endif
++		     Syntax error
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } >/dev/null && {
++	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       }; then
++  :
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++  # Broken: fails on valid input.
++continue
++fi
++
++rm -f conftest.err conftest.$ac_ext
++
++  # OK, works on sane cases.  Now check whether nonexistent headers
++  # can be detected and how.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <ac_nonexistent.h>
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } >/dev/null && {
++	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       }; then
++  # Broken: success on invalid input.
++continue
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++  # Passes both tests.
++ac_preproc_ok=:
++break
++fi
++
++rm -f conftest.err conftest.$ac_ext
++
++done
++# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
++rm -f conftest.err conftest.$ac_ext
++if $ac_preproc_ok; then
++  break
++fi
++
++    done
++    ac_cv_prog_CPP=$CPP
++
++fi
++  CPP=$ac_cv_prog_CPP
++else
++  ac_cv_prog_CPP=$CPP
++fi
++{ $as_echo "$as_me:$LINENO: result: $CPP" >&5
++$as_echo "$CPP" >&6; }
++ac_preproc_ok=false
++for ac_c_preproc_warn_flag in '' yes
++do
++  # Use a header file that comes with gcc, so configuring glibc
++  # with a fresh cross-compiler works.
++  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
++  # <limits.h> exists even on freestanding compilers.
++  # On the NeXT, cc -E runs the code through the compiler's parser,
++  # not just through cpp. "Syntax error" is here to catch this case.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#ifdef __STDC__
++# include <limits.h>
++#else
++# include <assert.h>
++#endif
++		     Syntax error
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } >/dev/null && {
++	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       }; then
++  :
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++  # Broken: fails on valid input.
++continue
++fi
++
++rm -f conftest.err conftest.$ac_ext
++
++  # OK, works on sane cases.  Now check whether nonexistent headers
++  # can be detected and how.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <ac_nonexistent.h>
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } >/dev/null && {
++	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       }; then
++  # Broken: success on invalid input.
++continue
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++  # Passes both tests.
++ac_preproc_ok=:
++break
++fi
++
++rm -f conftest.err conftest.$ac_ext
++
++done
++# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
++rm -f conftest.err conftest.$ac_ext
++if $ac_preproc_ok; then
++  :
++else
++  { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
++{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
++See \`config.log' for more details." >&5
++$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
++See \`config.log' for more details." >&2;}
++   { (exit 1); exit 1; }; }; }
++fi
++
++ac_ext=c
++ac_cpp='$CPP $CPPFLAGS'
++ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
++ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
++ac_compiler_gnu=$ac_cv_c_compiler_gnu
++
++
++{ $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5
++$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
++if test "${ac_cv_path_GREP+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  if test -z "$GREP"; then
++  ac_path_GREP_found=false
++  # Loop through the user's path and test for each of PROGNAME-LIST
++  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  for ac_prog in grep ggrep; do
++    for ac_exec_ext in '' $ac_executable_extensions; do
++      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
++      { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
++# Check for GNU ac_path_GREP and select it if it is found.
++  # Check for GNU $ac_path_GREP
++case `"$ac_path_GREP" --version 2>&1` in
++*GNU*)
++  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
++*)
++  ac_count=0
++  $as_echo_n 0123456789 >"conftest.in"
++  while :
++  do
++    cat "conftest.in" "conftest.in" >"conftest.tmp"
++    mv "conftest.tmp" "conftest.in"
++    cp "conftest.in" "conftest.nl"
++    $as_echo 'GREP' >> "conftest.nl"
++    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
++    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
++    ac_count=`expr $ac_count + 1`
++    if test $ac_count -gt ${ac_path_GREP_max-0}; then
++      # Best one so far, save it but keep looking for a better one
++      ac_cv_path_GREP="$ac_path_GREP"
++      ac_path_GREP_max=$ac_count
++    fi
++    # 10*(2^10) chars as input seems more than enough
++    test $ac_count -gt 10 && break
++  done
++  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
++esac
++
++      $ac_path_GREP_found && break 3
++    done
++  done
++done
++IFS=$as_save_IFS
++  if test -z "$ac_cv_path_GREP"; then
++    { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
++$as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
++   { (exit 1); exit 1; }; }
++  fi
++else
++  ac_cv_path_GREP=$GREP
++fi
++
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5
++$as_echo "$ac_cv_path_GREP" >&6; }
++ GREP="$ac_cv_path_GREP"
++
++
++{ $as_echo "$as_me:$LINENO: checking for egrep" >&5
++$as_echo_n "checking for egrep... " >&6; }
++if test "${ac_cv_path_EGREP+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
++   then ac_cv_path_EGREP="$GREP -E"
++   else
++     if test -z "$EGREP"; then
++  ac_path_EGREP_found=false
++  # Loop through the user's path and test for each of PROGNAME-LIST
++  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  for ac_prog in egrep; do
++    for ac_exec_ext in '' $ac_executable_extensions; do
++      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
++      { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
++# Check for GNU ac_path_EGREP and select it if it is found.
++  # Check for GNU $ac_path_EGREP
++case `"$ac_path_EGREP" --version 2>&1` in
++*GNU*)
++  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
++*)
++  ac_count=0
++  $as_echo_n 0123456789 >"conftest.in"
++  while :
++  do
++    cat "conftest.in" "conftest.in" >"conftest.tmp"
++    mv "conftest.tmp" "conftest.in"
++    cp "conftest.in" "conftest.nl"
++    $as_echo 'EGREP' >> "conftest.nl"
++    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
++    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
++    ac_count=`expr $ac_count + 1`
++    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
++      # Best one so far, save it but keep looking for a better one
++      ac_cv_path_EGREP="$ac_path_EGREP"
++      ac_path_EGREP_max=$ac_count
++    fi
++    # 10*(2^10) chars as input seems more than enough
++    test $ac_count -gt 10 && break
++  done
++  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
++esac
++
++      $ac_path_EGREP_found && break 3
++    done
++  done
++done
++IFS=$as_save_IFS
++  if test -z "$ac_cv_path_EGREP"; then
++    { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
++$as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
++   { (exit 1); exit 1; }; }
++  fi
++else
++  ac_cv_path_EGREP=$EGREP
++fi
++
++   fi
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5
++$as_echo "$ac_cv_path_EGREP" >&6; }
++ EGREP="$ac_cv_path_EGREP"
++
++
++{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5
++$as_echo_n "checking for ANSI C header files... " >&6; }
++if test "${ac_cv_header_stdc+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <stdlib.h>
++#include <stdarg.h>
++#include <string.h>
++#include <float.h>
++
++int
++main ()
++{
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_cv_header_stdc=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_cv_header_stdc=no
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++
++if test $ac_cv_header_stdc = yes; then
++  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <string.h>
++
++_ACEOF
++if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
++  $EGREP "memchr" >/dev/null 2>&1; then
++  :
++else
++  ac_cv_header_stdc=no
++fi
++rm -f conftest*
++
++fi
++
++if test $ac_cv_header_stdc = yes; then
++  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <stdlib.h>
++
++_ACEOF
++if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
++  $EGREP "free" >/dev/null 2>&1; then
++  :
++else
++  ac_cv_header_stdc=no
++fi
++rm -f conftest*
++
++fi
++
++if test $ac_cv_header_stdc = yes; then
++  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
++  if test "$cross_compiling" = yes; then
++  :
++else
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <ctype.h>
++#include <stdlib.h>
++#if ((' ' & 0x0FF) == 0x020)
++# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
++# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
++#else
++# define ISLOWER(c) \
++		   (('a' <= (c) && (c) <= 'i') \
++		     || ('j' <= (c) && (c) <= 'r') \
++		     || ('s' <= (c) && (c) <= 'z'))
++# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
++#endif
++
++#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
++int
++main ()
++{
++  int i;
++  for (i = 0; i < 256; i++)
++    if (XOR (islower (i), ISLOWER (i))
++	|| toupper (i) != TOUPPER (i))
++      return 2;
++  return 0;
++}
++_ACEOF
++rm -f conftest$ac_exeext
++if { (ac_try="$ac_link"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_link") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
++  { (case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_try") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; }; then
++  :
++else
++  $as_echo "$as_me: program exited with status $ac_status" >&5
++$as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++( exit $ac_status )
++ac_cv_header_stdc=no
++fi
++rm -rf conftest.dSYM
++rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
++fi
++
++
++fi
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
++$as_echo "$ac_cv_header_stdc" >&6; }
++if test $ac_cv_header_stdc = yes; then
++
++cat >>confdefs.h <<\_ACEOF
++#define STDC_HEADERS 1
++_ACEOF
++
++fi
++
++# On IRIX 5.3, sys/types and inttypes.h are conflicting.
++
++
++
++
++
++
++
++
++
++for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
++		  inttypes.h stdint.h unistd.h
++do
++as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
++{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
++$as_echo_n "checking for $ac_header... " >&6; }
++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
++  $as_echo_n "(cached) " >&6
++else
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++$ac_includes_default
++
++#include <$ac_header>
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  eval "$as_ac_Header=yes"
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	eval "$as_ac_Header=no"
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++fi
++ac_res=`eval 'as_val=${'$as_ac_Header'}
++		 $as_echo "$as_val"'`
++	       { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
++$as_echo "$ac_res" >&6; }
++as_val=`eval 'as_val=${'$as_ac_Header'}
++		 $as_echo "$as_val"'`
++   if test "x$as_val" = x""yes; then
++  cat >>confdefs.h <<_ACEOF
++#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
++_ACEOF
++
++fi
++
++done
++
++
++
++ { $as_echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5
++$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
++if test "${ac_cv_c_bigendian+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  ac_cv_c_bigendian=unknown
++    # See if we're dealing with a universal compiler.
++    cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#ifndef __APPLE_CC__
++	       not a universal capable compiler
++	     #endif
++	     typedef int dummy;
++
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++
++	# Check for potential -arch flags.  It is not universal unless
++	# there are some -arch flags.  Note that *ppc* also matches
++	# ppc64.  This check is also rather less than ideal.
++	case "${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS}" in  #(
++	  *-arch*ppc*|*-arch*i386*|*-arch*x86_64*) ac_cv_c_bigendian=universal;;
++	esac
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++    if test $ac_cv_c_bigendian = unknown; then
++      # See if sys/param.h defines the BYTE_ORDER macro.
++      cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <sys/types.h>
++	     #include <sys/param.h>
++
++int
++main ()
++{
++#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
++		     && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
++		     && LITTLE_ENDIAN)
++	      bogus endian macros
++	     #endif
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  # It does; now see whether it defined to BIG_ENDIAN or not.
++	 cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <sys/types.h>
++		#include <sys/param.h>
++
++int
++main ()
++{
++#if BYTE_ORDER != BIG_ENDIAN
++		 not big endian
++		#endif
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_cv_c_bigendian=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_cv_c_bigendian=no
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++    fi
++    if test $ac_cv_c_bigendian = unknown; then
++      # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
++      cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <limits.h>
++
++int
++main ()
++{
++#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
++	      bogus endian macros
++	     #endif
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  # It does; now see whether it defined to _BIG_ENDIAN or not.
++	 cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <limits.h>
++
++int
++main ()
++{
++#ifndef _BIG_ENDIAN
++		 not big endian
++		#endif
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_cv_c_bigendian=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_cv_c_bigendian=no
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++    fi
++    if test $ac_cv_c_bigendian = unknown; then
++      # Compile a test program.
++      if test "$cross_compiling" = yes; then
++  # Try to guess by grepping values from an object file.
++	 cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++short int ascii_mm[] =
++		  { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
++		short int ascii_ii[] =
++		  { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
++		int use_ascii (int i) {
++		  return ascii_mm[i] + ascii_ii[i];
++		}
++		short int ebcdic_ii[] =
++		  { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
++		short int ebcdic_mm[] =
++		  { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
++		int use_ebcdic (int i) {
++		  return ebcdic_mm[i] + ebcdic_ii[i];
++		}
++		extern int foo;
++
++int
++main ()
++{
++return use_ascii (foo) == use_ebcdic (foo);
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
++	      ac_cv_c_bigendian=yes
++	    fi
++	    if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
++	      if test "$ac_cv_c_bigendian" = unknown; then
++		ac_cv_c_bigendian=no
++	      else
++		# finding both strings is unlikely to happen, but who knows?
++		ac_cv_c_bigendian=unknown
++	      fi
++	    fi
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++else
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++$ac_includes_default
++int
++main ()
++{
++
++	     /* Are we little or big endian?  From Harbison&Steele.  */
++	     union
++	     {
++	       long int l;
++	       char c[sizeof (long int)];
++	     } u;
++	     u.l = 1;
++	     return u.c[sizeof (long int) - 1] == 1;
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest$ac_exeext
++if { (ac_try="$ac_link"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_link") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
++  { (case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_try") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; }; then
++  ac_cv_c_bigendian=no
++else
++  $as_echo "$as_me: program exited with status $ac_status" >&5
++$as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++( exit $ac_status )
++ac_cv_c_bigendian=yes
++fi
++rm -rf conftest.dSYM
++rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
++fi
++
++
++    fi
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5
++$as_echo "$ac_cv_c_bigendian" >&6; }
++ case $ac_cv_c_bigendian in #(
++   yes)
++     cat >>confdefs.h <<\_ACEOF
++#define WORDS_BIGENDIAN 1
++_ACEOF
++;; #(
++   no)
++      ;; #(
++   universal)
++
++cat >>confdefs.h <<\_ACEOF
++#define AC_APPLE_UNIVERSAL_BUILD 1
++_ACEOF
++
++     ;; #(
++   *)
++     { { $as_echo "$as_me:$LINENO: error: unknown endianness
++ presetting ac_cv_c_bigendian=no (or yes) will help" >&5
++$as_echo "$as_me: error: unknown endianness
++ presetting ac_cv_c_bigendian=no (or yes) will help" >&2;}
++   { (exit 1); exit 1; }; } ;;
++ esac
++
++
++ac_ext=c
++ac_cpp='$CPP $CPPFLAGS'
++ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
++ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
++ac_compiler_gnu=$ac_cv_c_compiler_gnu
++{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
++$as_echo_n "checking how to run the C preprocessor... " >&6; }
++# On Suns, sometimes $CPP names a directory.
++if test -n "$CPP" && test -d "$CPP"; then
++  CPP=
++fi
++if test -z "$CPP"; then
++  if test "${ac_cv_prog_CPP+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++      # Double quotes because CPP needs to be expanded
++    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
++    do
++      ac_preproc_ok=false
++for ac_c_preproc_warn_flag in '' yes
++do
++  # Use a header file that comes with gcc, so configuring glibc
++  # with a fresh cross-compiler works.
++  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
++  # <limits.h> exists even on freestanding compilers.
++  # On the NeXT, cc -E runs the code through the compiler's parser,
++  # not just through cpp. "Syntax error" is here to catch this case.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#ifdef __STDC__
++# include <limits.h>
++#else
++# include <assert.h>
++#endif
++		     Syntax error
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } >/dev/null && {
++	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       }; then
++  :
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++  # Broken: fails on valid input.
++continue
++fi
++
++rm -f conftest.err conftest.$ac_ext
++
++  # OK, works on sane cases.  Now check whether nonexistent headers
++  # can be detected and how.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <ac_nonexistent.h>
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } >/dev/null && {
++	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       }; then
++  # Broken: success on invalid input.
++continue
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++  # Passes both tests.
++ac_preproc_ok=:
++break
++fi
++
++rm -f conftest.err conftest.$ac_ext
++
++done
++# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
++rm -f conftest.err conftest.$ac_ext
++if $ac_preproc_ok; then
++  break
++fi
++
++    done
++    ac_cv_prog_CPP=$CPP
++
++fi
++  CPP=$ac_cv_prog_CPP
++else
++  ac_cv_prog_CPP=$CPP
++fi
++{ $as_echo "$as_me:$LINENO: result: $CPP" >&5
++$as_echo "$CPP" >&6; }
++ac_preproc_ok=false
++for ac_c_preproc_warn_flag in '' yes
++do
++  # Use a header file that comes with gcc, so configuring glibc
++  # with a fresh cross-compiler works.
++  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
++  # <limits.h> exists even on freestanding compilers.
++  # On the NeXT, cc -E runs the code through the compiler's parser,
++  # not just through cpp. "Syntax error" is here to catch this case.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#ifdef __STDC__
++# include <limits.h>
++#else
++# include <assert.h>
++#endif
++		     Syntax error
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } >/dev/null && {
++	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       }; then
++  :
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++  # Broken: fails on valid input.
++continue
++fi
++
++rm -f conftest.err conftest.$ac_ext
++
++  # OK, works on sane cases.  Now check whether nonexistent headers
++  # can be detected and how.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <ac_nonexistent.h>
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } >/dev/null && {
++	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       }; then
++  # Broken: success on invalid input.
++continue
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++  # Passes both tests.
++ac_preproc_ok=:
++break
++fi
++
++rm -f conftest.err conftest.$ac_ext
++
++done
++# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
++rm -f conftest.err conftest.$ac_ext
++if $ac_preproc_ok; then
++  :
++else
++  { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
++{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
++See \`config.log' for more details." >&5
++$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
++See \`config.log' for more details." >&2;}
++   { (exit 1); exit 1; }; }; }
++fi
++
++ac_ext=c
++ac_cpp='$CPP $CPPFLAGS'
++ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
++ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
++ac_compiler_gnu=$ac_cv_c_compiler_gnu
++
++# Find a good install program.  We prefer a C program (faster),
++# so one script is as good as another.  But avoid the broken or
++# incompatible versions:
++# SysV /etc/install, /usr/sbin/install
++# SunOS /usr/etc/install
++# IRIX /sbin/install
++# AIX /bin/install
++# AmigaOS /C/install, which installs bootblocks on floppy discs
++# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
++# AFS /usr/afsws/bin/install, which mishandles nonexistent args
++# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
++# OS/2's system install, which has a completely different semantic
++# ./install, which can be erroneously created by make from ./install.sh.
++# Reject install programs that cannot install multiple files.
++{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
++$as_echo_n "checking for a BSD-compatible install... " >&6; }
++if test -z "$INSTALL"; then
++if test "${ac_cv_path_install+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  # Account for people who put trailing slashes in PATH elements.
++case $as_dir/ in
++  ./ | .// | /cC/* | \
++  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
++  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
++  /usr/ucb/* ) ;;
++  *)
++    # OSF1 and SCO ODT 3.0 have their own names for install.
++    # Don't use installbsd from OSF since it installs stuff as root
++    # by default.
++    for ac_prog in ginstall scoinst install; do
++      for ac_exec_ext in '' $ac_executable_extensions; do
++	if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
++	  if test $ac_prog = install &&
++	    grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
++	    # AIX install.  It has an incompatible calling convention.
++	    :
++	  elif test $ac_prog = install &&
++	    grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
++	    # program-specific install script used by HP pwplus--don't use.
++	    :
++	  else
++	    rm -rf conftest.one conftest.two conftest.dir
++	    echo one > conftest.one
++	    echo two > conftest.two
++	    mkdir conftest.dir
++	    if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
++	      test -s conftest.one && test -s conftest.two &&
++	      test -s conftest.dir/conftest.one &&
++	      test -s conftest.dir/conftest.two
++	    then
++	      ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
++	      break 3
++	    fi
++	  fi
++	fi
++      done
++    done
++    ;;
++esac
++
++done
++IFS=$as_save_IFS
++
++rm -rf conftest.one conftest.two conftest.dir
++
++fi
++  if test "${ac_cv_path_install+set}" = set; then
++    INSTALL=$ac_cv_path_install
++  else
++    # As a last resort, use the slow shell script.  Don't cache a
++    # value for INSTALL within a source directory, because that will
++    # break other packages using the cache if that directory is
++    # removed, or if the value is a relative name.
++    INSTALL=$ac_install_sh
++  fi
++fi
++{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5
++$as_echo "$INSTALL" >&6; }
++
++# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
++# It thinks the first close brace ends the variable substitution.
++test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
++
++test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
++
++test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
++
++{ $as_echo "$as_me:$LINENO: checking whether ln -s works" >&5
++$as_echo_n "checking whether ln -s works... " >&6; }
++LN_S=$as_ln_s
++if test "$LN_S" = "ln -s"; then
++  { $as_echo "$as_me:$LINENO: result: yes" >&5
++$as_echo "yes" >&6; }
++else
++  { $as_echo "$as_me:$LINENO: result: no, using $LN_S" >&5
++$as_echo "no, using $LN_S" >&6; }
++fi
++
++{ $as_echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
++$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
++set x ${MAKE-make}
++ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
++if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then
++  $as_echo_n "(cached) " >&6
++else
++  cat >conftest.make <<\_ACEOF
++SHELL = /bin/sh
++all:
++	@echo '@@@%%%=$(MAKE)=@@@%%%'
++_ACEOF
++# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
++case `${MAKE-make} -f conftest.make 2>/dev/null` in
++  *@@@%%%=?*=@@@%%%*)
++    eval ac_cv_prog_make_${ac_make}_set=yes;;
++  *)
++    eval ac_cv_prog_make_${ac_make}_set=no;;
++esac
++rm -f conftest.make
++fi
++if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
++  { $as_echo "$as_me:$LINENO: result: yes" >&5
++$as_echo "yes" >&6; }
++  SET_MAKE=
++else
++  { $as_echo "$as_me:$LINENO: result: no" >&5
++$as_echo "no" >&6; }
++  SET_MAKE="MAKE=${MAKE-make}"
++fi
++
++# Extract the first word of "sh", so it can be a program name with args.
++set dummy sh; ac_word=$2
++{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
++$as_echo_n "checking for $ac_word... " >&6; }
++if test "${ac_cv_path_SH+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  case $SH in
++  [\\/]* | ?:[\\/]*)
++  ac_cv_path_SH="$SH" # Let the user override the test with a path.
++  ;;
++  *)
++  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  for ac_exec_ext in '' $ac_executable_extensions; do
++  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
++    ac_cv_path_SH="$as_dir/$ac_word$ac_exec_ext"
++    $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
++    break 2
++  fi
++done
++done
++IFS=$as_save_IFS
++
++  ;;
++esac
++fi
++SH=$ac_cv_path_SH
++if test -n "$SH"; then
++  { $as_echo "$as_me:$LINENO: result: $SH" >&5
++$as_echo "$SH" >&6; }
++else
++  { $as_echo "$as_me:$LINENO: result: no" >&5
++$as_echo "no" >&6; }
++fi
++
++
++
++{ $as_echo "$as_me:$LINENO: checking for inline" >&5
++$as_echo_n "checking for inline... " >&6; }
++if test "${ac_cv_c_inline+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  ac_cv_c_inline=no
++for ac_kw in inline __inline__ __inline; do
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#ifndef __cplusplus
++typedef int foo_t;
++static $ac_kw foo_t static_foo () {return 0; }
++$ac_kw foo_t foo () {return 0; }
++#endif
++
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_cv_c_inline=$ac_kw
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++  test "$ac_cv_c_inline" != no && break
++done
++
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5
++$as_echo "$ac_cv_c_inline" >&6; }
++
++
++case $ac_cv_c_inline in
++  inline | yes) ;;
++  *)
++    case $ac_cv_c_inline in
++      no) ac_val=;;
++      *) ac_val=$ac_cv_c_inline;;
++    esac
++    cat >>confdefs.h <<_ACEOF
++#ifndef __cplusplus
++#define inline $ac_val
++#endif
++_ACEOF
++    ;;
++esac
++
++if test "$GCC" = "yes"; then
++        CFLAGS="-Wall -fno-strict-aliasing $CFLAGS"
++	CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
++fi
++
++{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5
++$as_echo_n "checking for ANSI C header files... " >&6; }
++if test "${ac_cv_header_stdc+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <stdlib.h>
++#include <stdarg.h>
++#include <string.h>
++#include <float.h>
++
++int
++main ()
++{
++
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_cv_header_stdc=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_cv_header_stdc=no
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++
++if test $ac_cv_header_stdc = yes; then
++  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <string.h>
++
++_ACEOF
++if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
++  $EGREP "memchr" >/dev/null 2>&1; then
++  :
++else
++  ac_cv_header_stdc=no
++fi
++rm -f conftest*
++
++fi
++
++if test $ac_cv_header_stdc = yes; then
++  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <stdlib.h>
++
++_ACEOF
++if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
++  $EGREP "free" >/dev/null 2>&1; then
++  :
++else
++  ac_cv_header_stdc=no
++fi
++rm -f conftest*
++
++fi
++
++if test $ac_cv_header_stdc = yes; then
++  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
++  if test "$cross_compiling" = yes; then
++  :
++else
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <ctype.h>
++#include <stdlib.h>
++#if ((' ' & 0x0FF) == 0x020)
++# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
++# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
++#else
++# define ISLOWER(c) \
++		   (('a' <= (c) && (c) <= 'i') \
++		     || ('j' <= (c) && (c) <= 'r') \
++		     || ('s' <= (c) && (c) <= 'z'))
++# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
++#endif
++
++#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
++int
++main ()
++{
++  int i;
++  for (i = 0; i < 256; i++)
++    if (XOR (islower (i), ISLOWER (i))
++	|| toupper (i) != TOUPPER (i))
++      return 2;
++  return 0;
++}
++_ACEOF
++rm -f conftest$ac_exeext
++if { (ac_try="$ac_link"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_link") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
++  { (case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_try") 2>&5
++  ac_status=$?
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; }; then
++  :
++else
++  $as_echo "$as_me: program exited with status $ac_status" >&5
++$as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++( exit $ac_status )
++ac_cv_header_stdc=no
++fi
++rm -rf conftest.dSYM
++rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
++fi
++
++
++fi
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
++$as_echo "$ac_cv_header_stdc" >&6; }
++if test $ac_cv_header_stdc = yes; then
++
++cat >>confdefs.h <<\_ACEOF
++#define STDC_HEADERS 1
++_ACEOF
++
++fi
++
++{ $as_echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5
++$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
++if test "${ac_cv_header_sys_wait_h+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <sys/types.h>
++#include <sys/wait.h>
++#ifndef WEXITSTATUS
++# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
++#endif
++#ifndef WIFEXITED
++# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
++#endif
++
++int
++main ()
++{
++  int s;
++  wait (&s);
++  s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_cv_header_sys_wait_h=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_cv_header_sys_wait_h=no
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5
++$as_echo "$ac_cv_header_sys_wait_h" >&6; }
++if test $ac_cv_header_sys_wait_h = yes; then
++
++cat >>confdefs.h <<\_ACEOF
++#define HAVE_SYS_WAIT_H 1
++_ACEOF
++
++fi
++
++
++
++
++
++
++
++
++
++
++
++for ac_header in errno.h fcntl.h malloc.h stdlib.h string.h strings.h sys/time.h unistd.h locale.h getopt.h
++do
++as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
++  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
++$as_echo_n "checking for $ac_header... " >&6; }
++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
++  $as_echo_n "(cached) " >&6
++fi
++ac_res=`eval 'as_val=${'$as_ac_Header'}
++		 $as_echo "$as_val"'`
++	       { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
++$as_echo "$ac_res" >&6; }
++else
++  # Is the header compilable?
++{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
++$as_echo_n "checking $ac_header usability... " >&6; }
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++$ac_includes_default
++#include <$ac_header>
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_header_compiler=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_header_compiler=no
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
++$as_echo "$ac_header_compiler" >&6; }
++
++# Is the header present?
++{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
++$as_echo_n "checking $ac_header presence... " >&6; }
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <$ac_header>
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } >/dev/null && {
++	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       }; then
++  ac_header_preproc=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++  ac_header_preproc=no
++fi
++
++rm -f conftest.err conftest.$ac_ext
++{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
++$as_echo "$ac_header_preproc" >&6; }
++
++# So?  What about this header?
++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
++  yes:no: )
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
++$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
++$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
++    ac_header_preproc=yes
++    ;;
++  no:yes:* )
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
++$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
++$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
++$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
++$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
++$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
++$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
++
++    ;;
++esac
++{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
++$as_echo_n "checking for $ac_header... " >&6; }
++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
++  $as_echo_n "(cached) " >&6
++else
++  eval "$as_ac_Header=\$ac_header_preproc"
++fi
++ac_res=`eval 'as_val=${'$as_ac_Header'}
++		 $as_echo "$as_val"'`
++	       { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
++$as_echo "$ac_res" >&6; }
++
++fi
++as_val=`eval 'as_val=${'$as_ac_Header'}
++		 $as_echo "$as_val"'`
++   if test "x$as_val" = x""yes; then
++  cat >>confdefs.h <<_ACEOF
++#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
++_ACEOF
++
++fi
++
++done
++
++
++# Check if socket() is in libsocket
++{ $as_echo "$as_me:$LINENO: checking for socket in -lsocket" >&5
++$as_echo_n "checking for socket in -lsocket... " >&6; }
++if test "${ac_cv_lib_socket_socket+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  ac_check_lib_save_LIBS=$LIBS
++LIBS="-lsocket  $LIBS"
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++
++/* Override any GCC internal prototype to avoid an error.
++   Use char because int might match the return type of a GCC
++   builtin and then its argument prototype would still apply.  */
++#ifdef __cplusplus
++extern "C"
++#endif
++char socket ();
++int
++main ()
++{
++return socket ();
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext conftest$ac_exeext
++if { (ac_try="$ac_link"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_link") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest$ac_exeext && {
++	 test "$cross_compiling" = yes ||
++	 $as_test_x conftest$ac_exeext
++       }; then
++  ac_cv_lib_socket_socket=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_cv_lib_socket_socket=no
++fi
++
++rm -rf conftest.dSYM
++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
++      conftest$ac_exeext conftest.$ac_ext
++LIBS=$ac_check_lib_save_LIBS
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_socket_socket" >&5
++$as_echo "$ac_cv_lib_socket_socket" >&6; }
++if test "x$ac_cv_lib_socket_socket" = x""yes; then
++  LIBS="$LIBS -lsocket"
++fi
++
++
++
++
++{ $as_echo "$as_me:$LINENO: checking for getopt_long" >&5
++$as_echo_n "checking for getopt_long... " >&6; }
++if test "${ac_cv_func_getopt_long+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++/* Define getopt_long to an innocuous variant, in case <limits.h> declares getopt_long.
++   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
++#define getopt_long innocuous_getopt_long
++
++/* System header to define __stub macros and hopefully few prototypes,
++    which can conflict with char getopt_long (); below.
++    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
++    <limits.h> exists even on freestanding compilers.  */
++
++#ifdef __STDC__
++# include <limits.h>
++#else
++# include <assert.h>
++#endif
++
++#undef getopt_long
++
++/* Override any GCC internal prototype to avoid an error.
++   Use char because int might match the return type of a GCC
++   builtin and then its argument prototype would still apply.  */
++#ifdef __cplusplus
++extern "C"
++#endif
++char getopt_long ();
++/* The GNU C library defines this for functions which it implements
++    to always fail with ENOSYS.  Some functions are actually named
++    something starting with __ and the normal name is an alias.  */
++#if defined __stub_getopt_long || defined __stub___getopt_long
++choke me
++#endif
++
++int
++main ()
++{
++return getopt_long ();
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext conftest$ac_exeext
++if { (ac_try="$ac_link"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_link") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest$ac_exeext && {
++	 test "$cross_compiling" = yes ||
++	 $as_test_x conftest$ac_exeext
++       }; then
++  ac_cv_func_getopt_long=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_cv_func_getopt_long=no
++fi
++
++rm -rf conftest.dSYM
++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
++      conftest$ac_exeext conftest.$ac_ext
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_getopt_long" >&5
++$as_echo "$ac_cv_func_getopt_long" >&6; }
++if test "x$ac_cv_func_getopt_long" = x""yes; then
++
++cat >>confdefs.h <<\_ACEOF
++#define HAVE_GETOPT_LONG 1
++_ACEOF
++
++else
++  GETOPTSRC="$GETOPTSRC \$(top_srcdir)/compat/my_getopt.c"
++		CPPFLAGS="-I\$(top_srcdir)/compat/ $CPPFLAGS"
++
++cat >>confdefs.h <<\_ACEOF
++#define HAVE_GETOPT_H 1
++_ACEOF
++
++fi
++
++
++WITH_SECURITY=maybe
++
++# Check whether --with-security was given.
++if test "${with_security+set}" = set; then
++  withval=$with_security;
++		if test "x$withval" = "xno" -o "x$withval" = "xyes"; then
++			WITH_SECURITY=$withval
++		else
++			WITH_SECURITY=yes
++			CPPFLAGS="$CPPFLAGS -I${withval}"
++			LDFLAGS="$LDFLAGS -L${withval}"
++		fi
++
++
++fi
++
++
++if test "x$WITH_SECURITY" != "xno" ; then
++	# Check for openssl support - very primitive, we just
++	# check for the presence of crypto.h
++
++for ac_header in openssl/crypto.h
++do
++as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
++  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
++$as_echo_n "checking for $ac_header... " >&6; }
++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
++  $as_echo_n "(cached) " >&6
++fi
++ac_res=`eval 'as_val=${'$as_ac_Header'}
++		 $as_echo "$as_val"'`
++	       { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
++$as_echo "$ac_res" >&6; }
++else
++  # Is the header compilable?
++{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
++$as_echo_n "checking $ac_header usability... " >&6; }
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++$ac_includes_default
++#include <$ac_header>
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_header_compiler=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_header_compiler=no
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
++$as_echo "$ac_header_compiler" >&6; }
++
++# Is the header present?
++{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
++$as_echo_n "checking $ac_header presence... " >&6; }
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <$ac_header>
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } >/dev/null && {
++	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       }; then
++  ac_header_preproc=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++  ac_header_preproc=no
++fi
++
++rm -f conftest.err conftest.$ac_ext
++{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
++$as_echo "$ac_header_preproc" >&6; }
++
++# So?  What about this header?
++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
++  yes:no: )
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
++$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
++$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
++    ac_header_preproc=yes
++    ;;
++  no:yes:* )
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
++$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
++$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
++$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
++$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
++$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
++$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
++
++    ;;
++esac
++{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
++$as_echo_n "checking for $ac_header... " >&6; }
++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
++  $as_echo_n "(cached) " >&6
++else
++  eval "$as_ac_Header=\$ac_header_preproc"
++fi
++ac_res=`eval 'as_val=${'$as_ac_Header'}
++		 $as_echo "$as_val"'`
++	       { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
++$as_echo "$ac_res" >&6; }
++
++fi
++as_val=`eval 'as_val=${'$as_ac_Header'}
++		 $as_echo "$as_val"'`
++   if test "x$as_val" = x""yes; then
++  cat >>confdefs.h <<_ACEOF
++#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
++_ACEOF
++
++else
++  have_libcrypto=no
++fi
++
++done
++
++	{ $as_echo "$as_me:$LINENO: checking for EVP_PKEY_new in -lcrypto" >&5
++$as_echo_n "checking for EVP_PKEY_new in -lcrypto... " >&6; }
++if test "${ac_cv_lib_crypto_EVP_PKEY_new+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  ac_check_lib_save_LIBS=$LIBS
++LIBS="-lcrypto  $LIBS"
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++
++/* Override any GCC internal prototype to avoid an error.
++   Use char because int might match the return type of a GCC
++   builtin and then its argument prototype would still apply.  */
++#ifdef __cplusplus
++extern "C"
++#endif
++char EVP_PKEY_new ();
++int
++main ()
++{
++return EVP_PKEY_new ();
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext conftest$ac_exeext
++if { (ac_try="$ac_link"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_link") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest$ac_exeext && {
++	 test "$cross_compiling" = yes ||
++	 $as_test_x conftest$ac_exeext
++       }; then
++  ac_cv_lib_crypto_EVP_PKEY_new=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_cv_lib_crypto_EVP_PKEY_new=no
++fi
++
++rm -rf conftest.dSYM
++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
++      conftest$ac_exeext conftest.$ac_ext
++LIBS=$ac_check_lib_save_LIBS
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_crypto_EVP_PKEY_new" >&5
++$as_echo "$ac_cv_lib_crypto_EVP_PKEY_new" >&6; }
++if test "x$ac_cv_lib_crypto_EVP_PKEY_new" = x""yes; then
++  SECLIBS="-lcrypto"
++else
++  have_libcrypto=no
++fi
++
++
++	if test "x$have_libcrypto" != "xno" ; then
++
++cat >>confdefs.h <<\_ACEOF
++#define WITH_SECURITY 1
++_ACEOF
++
++	else
++		if test "x$WITH_SECURITY" = "xyes" ; then
++			{ { $as_echo "$as_me:$LINENO: error: Security requested, but unable to find libcrypto" >&5
++$as_echo "$as_me: error: Security requested, but unable to find libcrypto" >&2;}
++   { (exit 1); exit 1; }; }
++		fi
++	fi
++fi
++
++
++WITH_SLP=maybe
++
++# Check whether --with-slp was given.
++if test "${with_slp+set}" = set; then
++  withval=$with_slp;
++		if test "x$withval" = "xno" -o "x$withval" = "xyes"; then
++			WITH_SLP=$withval
++		else
++			WITH_SLP=yes
++			CPPFLAGS="$CPPFLAGS -I${withval}"
++			LDFLAGS="$LDFLAGS -L${withval}"
++		fi
++
++
++fi
++
++
++if test "x$WITH_SLP" != "xno" ; then
++	# Check for openslp support - very primitive
++
++for ac_header in slp.h
++do
++as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
++  { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
++$as_echo_n "checking for $ac_header... " >&6; }
++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
++  $as_echo_n "(cached) " >&6
++fi
++ac_res=`eval 'as_val=${'$as_ac_Header'}
++		 $as_echo "$as_val"'`
++	       { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
++$as_echo "$ac_res" >&6; }
++else
++  # Is the header compilable?
++{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
++$as_echo_n "checking $ac_header usability... " >&6; }
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++$ac_includes_default
++#include <$ac_header>
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_compile") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest.$ac_objext; then
++  ac_header_compiler=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_header_compiler=no
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
++$as_echo "$ac_header_compiler" >&6; }
++
++# Is the header present?
++{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
++$as_echo_n "checking $ac_header presence... " >&6; }
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <$ac_header>
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } >/dev/null && {
++	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       }; then
++  ac_header_preproc=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++  ac_header_preproc=no
++fi
++
++rm -f conftest.err conftest.$ac_ext
++{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
++$as_echo "$ac_header_preproc" >&6; }
++
++# So?  What about this header?
++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
++  yes:no: )
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
++$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
++$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
++    ac_header_preproc=yes
++    ;;
++  no:yes:* )
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
++$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
++$as_echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
++$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
++$as_echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
++$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
++    { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
++$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
++
++    ;;
++esac
++{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
++$as_echo_n "checking for $ac_header... " >&6; }
++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
++  $as_echo_n "(cached) " >&6
++else
++  eval "$as_ac_Header=\$ac_header_preproc"
++fi
++ac_res=`eval 'as_val=${'$as_ac_Header'}
++		 $as_echo "$as_val"'`
++	       { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
++$as_echo "$ac_res" >&6; }
++
++fi
++as_val=`eval 'as_val=${'$as_ac_Header'}
++		 $as_echo "$as_val"'`
++   if test "x$as_val" = x""yes; then
++  cat >>confdefs.h <<_ACEOF
++#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
++_ACEOF
++
++else
++  have_openslp=no
++fi
++
++done
++
++	{ $as_echo "$as_me:$LINENO: checking for SLPOpen in -lslp" >&5
++$as_echo_n "checking for SLPOpen in -lslp... " >&6; }
++if test "${ac_cv_lib_slp_SLPOpen+set}" = set; then
++  $as_echo_n "(cached) " >&6
++else
++  ac_check_lib_save_LIBS=$LIBS
++LIBS="-lslp  $LIBS"
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++
++/* Override any GCC internal prototype to avoid an error.
++   Use char because int might match the return type of a GCC
++   builtin and then its argument prototype would still apply.  */
++#ifdef __cplusplus
++extern "C"
++#endif
++char SLPOpen ();
++int
++main ()
++{
++return SLPOpen ();
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext conftest$ac_exeext
++if { (ac_try="$ac_link"
++case "(($ac_try" in
++  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++  *) ac_try_echo=$ac_try;;
++esac
++eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
++$as_echo "$ac_try_echo") >&5
++  (eval "$ac_link") 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } && {
++	 test -z "$ac_c_werror_flag" ||
++	 test ! -s conftest.err
++       } && test -s conftest$ac_exeext && {
++	 test "$cross_compiling" = yes ||
++	 $as_test_x conftest$ac_exeext
++       }; then
++  ac_cv_lib_slp_SLPOpen=yes
++else
++  $as_echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++	ac_cv_lib_slp_SLPOpen=no
++fi
++
++rm -rf conftest.dSYM
++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
++      conftest$ac_exeext conftest.$ac_ext
++LIBS=$ac_check_lib_save_LIBS
++fi
++{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_slp_SLPOpen" >&5
++$as_echo "$ac_cv_lib_slp_SLPOpen" >&6; }
++if test "x$ac_cv_lib_slp_SLPOpen" = x""yes; then
++  SLPLIBS="-lslp"
++else
++  have_openslp=no
++fi
++
++
++	if test "x$have_openslp" != "xno" ; then
++
++cat >>confdefs.h <<\_ACEOF
++#define WITH_SLP 1
++_ACEOF
++
++	else
++		if test "x$WITH_SLP" = "xyes" ; then
++			{ { $as_echo "$as_me:$LINENO: error: SLP requested, but unable to find openslp" >&5
++$as_echo "$as_me: error: SLP requested, but unable to find openslp" >&2;}
++   { (exit 1); exit 1; }; }
++		fi
++	fi
++fi
++
++
++MEMDEBUG=
++# Check whether --enable-memdebug was given.
++if test "${enable_memdebug+set}" = set; then
++  enableval=$enable_memdebug;
++		if test "x$enableval" = "xyes" ; then
++			CPPFLAGS="$CPPFLAGS -DMEMDEBUG"
++		fi
++
++
++fi
++
++
++
++ac_config_files="$ac_config_files Makefile"
++
++cat >confcache <<\_ACEOF
++# This file is a shell script that caches the results of configure
++# tests run on this system so they can be shared between configure
++# scripts and configure runs, see configure's option --config-cache.
++# It is not useful on other systems.  If it contains results you don't
++# want to keep, you may remove or edit it.
++#
++# config.status only pays attention to the cache file if you give it
++# the --recheck option to rerun configure.
++#
++# `ac_cv_env_foo' variables (set or unset) will be overridden when
++# loading this file, other *unset* `ac_cv_foo' will be assigned the
++# following values.
++
++_ACEOF
++
++# The following way of writing the cache mishandles newlines in values,
++# but we know of no workaround that is simple, portable, and efficient.
++# So, we kill variables containing newlines.
++# Ultrix sh set writes to stderr and can't be redirected directly,
++# and sets the high bit in the cache file unless we assign to the vars.
++(
++  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
++    eval ac_val=\$$ac_var
++    case $ac_val in #(
++    *${as_nl}*)
++      case $ac_var in #(
++      *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
++$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
++      esac
++      case $ac_var in #(
++      _ | IFS | as_nl) ;; #(
++      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
++      *) $as_unset $ac_var ;;
++      esac ;;
++    esac
++  done
++
++  (set) 2>&1 |
++    case $as_nl`(ac_space=' '; set) 2>&1` in #(
++    *${as_nl}ac_space=\ *)
++      # `set' does not quote correctly, so add quotes (double-quote
++      # substitution turns \\\\ into \\, and sed turns \\ into \).
++      sed -n \
++	"s/'/'\\\\''/g;
++	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
++      ;; #(
++    *)
++      # `set' quotes correctly as required by POSIX, so do not add quotes.
++      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
++      ;;
++    esac |
++    sort
++) |
++  sed '
++     /^ac_cv_env_/b end
++     t clear
++     :clear
++     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
++     t end
++     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
++     :end' >>confcache
++if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
++  if test -w "$cache_file"; then
++    test "x$cache_file" != "x/dev/null" &&
++      { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5
++$as_echo "$as_me: updating cache $cache_file" >&6;}
++    cat confcache >$cache_file
++  else
++    { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
++$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
++  fi
++fi
++rm -f confcache
++
++test "x$prefix" = xNONE && prefix=$ac_default_prefix
++# Let make expand exec_prefix.
++test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
++
++DEFS=-DHAVE_CONFIG_H
++
++ac_libobjs=
++ac_ltlibobjs=
++for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
++  # 1. Remove the extension, and $U if already installed.
++  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
++  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
++  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
++  #    will be set to the directory where LIBOBJS objects are built.
++  ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
++  ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'
++done
++LIBOBJS=$ac_libobjs
++
++LTLIBOBJS=$ac_ltlibobjs
++
++
++
++
++: ${CONFIG_STATUS=./config.status}
++ac_write_fail=0
++ac_clean_files_save=$ac_clean_files
++ac_clean_files="$ac_clean_files $CONFIG_STATUS"
++{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
++$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
++cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
++#! $SHELL
++# Generated by $as_me.
++# Run this file to recreate the current configuration.
++# Compiler output produced by configure, useful for debugging
++# configure, is in config.log if it exists.
++
++debug=false
++ac_cs_recheck=false
++ac_cs_silent=false
++SHELL=\${CONFIG_SHELL-$SHELL}
++_ACEOF
++
++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
++## --------------------- ##
++## M4sh Initialization.  ##
++## --------------------- ##
++
++# Be more Bourne compatible
++DUALCASE=1; export DUALCASE # for MKS sh
++if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
++  emulate sh
++  NULLCMD=:
++  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
++  # is contrary to our usage.  Disable this feature.
++  alias -g '${1+"$@"}'='"$@"'
++  setopt NO_GLOB_SUBST
++else
++  case `(set -o) 2>/dev/null` in
++  *posix*) set -o posix ;;
++esac
++
++fi
++
++
++
++
++# PATH needs CR
++# Avoid depending upon Character Ranges.
++as_cr_letters='abcdefghijklmnopqrstuvwxyz'
++as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
++as_cr_Letters=$as_cr_letters$as_cr_LETTERS
++as_cr_digits='0123456789'
++as_cr_alnum=$as_cr_Letters$as_cr_digits
++
++as_nl='
++'
++export as_nl
++# Printing a long string crashes Solaris 7 /usr/bin/printf.
++as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
++as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
++as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
++if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
++  as_echo='printf %s\n'
++  as_echo_n='printf %s'
++else
++  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
++    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
++    as_echo_n='/usr/ucb/echo -n'
++  else
++    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
++    as_echo_n_body='eval
++      arg=$1;
++      case $arg in
++      *"$as_nl"*)
++	expr "X$arg" : "X\\(.*\\)$as_nl";
++	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
++      esac;
++      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
++    '
++    export as_echo_n_body
++    as_echo_n='sh -c $as_echo_n_body as_echo'
++  fi
++  export as_echo_body
++  as_echo='sh -c $as_echo_body as_echo'
++fi
++
++# The user is always right.
++if test "${PATH_SEPARATOR+set}" != set; then
++  PATH_SEPARATOR=:
++  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
++    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
++      PATH_SEPARATOR=';'
++  }
++fi
++
++# Support unset when possible.
++if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
++  as_unset=unset
++else
++  as_unset=false
++fi
++
++
++# IFS
++# We need space, tab and new line, in precisely that order.  Quoting is
++# there to prevent editors from complaining about space-tab.
++# (If _AS_PATH_WALK were called with IFS unset, it would disable word
++# splitting by setting IFS to empty value.)
++IFS=" ""	$as_nl"
++
++# Find who we are.  Look in the path if we contain no directory separator.
++case $0 in
++  *[\\/]* ) as_myself=$0 ;;
++  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++  IFS=$as_save_IFS
++  test -z "$as_dir" && as_dir=.
++  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
++done
++IFS=$as_save_IFS
++
++     ;;
++esac
++# We did not find ourselves, most probably we were run as `sh COMMAND'
++# in which case we are not to be found in the path.
++if test "x$as_myself" = x; then
++  as_myself=$0
++fi
++if test ! -f "$as_myself"; then
++  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
++  { (exit 1); exit 1; }
++fi
++
++# Work around bugs in pre-3.0 UWIN ksh.
++for as_var in ENV MAIL MAILPATH
++do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
++done
++PS1='$ '
++PS2='> '
++PS4='+ '
++
++# NLS nuisances.
++LC_ALL=C
++export LC_ALL
++LANGUAGE=C
++export LANGUAGE
++
++# Required to use basename.
++if expr a : '\(a\)' >/dev/null 2>&1 &&
++   test "X`expr 00001 : '.*\(...\)'`" = X001; then
++  as_expr=expr
++else
++  as_expr=false
++fi
++
++if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
++  as_basename=basename
++else
++  as_basename=false
++fi
++
++
++# Name of the executable.
++as_me=`$as_basename -- "$0" ||
++$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
++	 X"$0" : 'X\(//\)$' \| \
++	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
++$as_echo X/"$0" |
++    sed '/^.*\/\([^/][^/]*\)\/*$/{
++	    s//\1/
++	    q
++	  }
++	  /^X\/\(\/\/\)$/{
++	    s//\1/
++	    q
++	  }
++	  /^X\/\(\/\).*/{
++	    s//\1/
++	    q
++	  }
++	  s/.*/./; q'`
++
++# CDPATH.
++$as_unset CDPATH
++
++
++
++  as_lineno_1=$LINENO
++  as_lineno_2=$LINENO
++  test "x$as_lineno_1" != "x$as_lineno_2" &&
++  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
++
++  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
++  # uniformly replaced by the line number.  The first 'sed' inserts a
++  # line-number line after each line using $LINENO; the second 'sed'
++  # does the real work.  The second script uses 'N' to pair each
++  # line-number line with the line containing $LINENO, and appends
++  # trailing '-' during substitution so that $LINENO is not a special
++  # case at line end.
++  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
++  # scripts with optimization help from Paolo Bonzini.  Blame Lee
++  # E. McMahon (1931-1989) for sed's syntax.  :-)
++  sed -n '
++    p
++    /[$]LINENO/=
++  ' <$as_myself |
++    sed '
++      s/[$]LINENO.*/&-/
++      t lineno
++      b
++      :lineno
++      N
++      :loop
++      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
++      t loop
++      s/-\n.*//
++    ' >$as_me.lineno &&
++  chmod +x "$as_me.lineno" ||
++    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
++   { (exit 1); exit 1; }; }
++
++  # Don't try to exec as it changes $[0], causing all sort of problems
++  # (the dirname of $[0] is not the place where we might find the
++  # original and so on.  Autoconf is especially sensitive to this).
++  . "./$as_me.lineno"
++  # Exit status is that of the last command.
++  exit
++}
++
++
++if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
++  as_dirname=dirname
++else
++  as_dirname=false
++fi
++
++ECHO_C= ECHO_N= ECHO_T=
++case `echo -n x` in
++-n*)
++  case `echo 'x\c'` in
++  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
++  *)   ECHO_C='\c';;
++  esac;;
++*)
++  ECHO_N='-n';;
++esac
++if expr a : '\(a\)' >/dev/null 2>&1 &&
++   test "X`expr 00001 : '.*\(...\)'`" = X001; then
++  as_expr=expr
++else
++  as_expr=false
++fi
++
++rm -f conf$$ conf$$.exe conf$$.file
++if test -d conf$$.dir; then
++  rm -f conf$$.dir/conf$$.file
++else
++  rm -f conf$$.dir
++  mkdir conf$$.dir 2>/dev/null
++fi
++if (echo >conf$$.file) 2>/dev/null; then
++  if ln -s conf$$.file conf$$ 2>/dev/null; then
++    as_ln_s='ln -s'
++    # ... but there are two gotchas:
++    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
++    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
++    # In both cases, we have to default to `cp -p'.
++    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
++      as_ln_s='cp -p'
++  elif ln conf$$.file conf$$ 2>/dev/null; then
++    as_ln_s=ln
++  else
++    as_ln_s='cp -p'
++  fi
++else
++  as_ln_s='cp -p'
++fi
++rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
++rmdir conf$$.dir 2>/dev/null
++
++if mkdir -p . 2>/dev/null; then
++  as_mkdir_p=:
++else
++  test -d ./-p && rmdir ./-p
++  as_mkdir_p=false
++fi
++
++if test -x / >/dev/null 2>&1; then
++  as_test_x='test -x'
++else
++  if ls -dL / >/dev/null 2>&1; then
++    as_ls_L_option=L
++  else
++    as_ls_L_option=
++  fi
++  as_test_x='
++    eval sh -c '\''
++      if test -d "$1"; then
++	test -d "$1/.";
++      else
++	case $1 in
++	-*)set "./$1";;
++	esac;
++	case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
++	???[sx]*):;;*)false;;esac;fi
++    '\'' sh
++  '
++fi
++as_executable_p=$as_test_x
++
++# Sed expression to map a string onto a valid CPP name.
++as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
++
++# Sed expression to map a string onto a valid variable name.
++as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
++
++
++exec 6>&1
++
++# Save the log message, to keep $[0] and so on meaningful, and to
++# report actual input values of CONFIG_FILES etc. instead of their
++# values after options handling.
++ac_log="
++This file was extended by open-isns $as_me 0.90, which was
++generated by GNU Autoconf 2.63.  Invocation command line was
++
++  CONFIG_FILES    = $CONFIG_FILES
++  CONFIG_HEADERS  = $CONFIG_HEADERS
++  CONFIG_LINKS    = $CONFIG_LINKS
++  CONFIG_COMMANDS = $CONFIG_COMMANDS
++  $ $0 $@
++
++on `(hostname || uname -n) 2>/dev/null | sed 1q`
++"
++
++_ACEOF
++
++case $ac_config_files in *"
++"*) set x $ac_config_files; shift; ac_config_files=$*;;
++esac
++
++case $ac_config_headers in *"
++"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
++esac
++
++
++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
++# Files that config.status was made for.
++config_files="$ac_config_files"
++config_headers="$ac_config_headers"
++
++_ACEOF
++
++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
++ac_cs_usage="\
++\`$as_me' instantiates files from templates according to the
++current configuration.
++
++Usage: $0 [OPTION]... [FILE]...
++
++  -h, --help       print this help, then exit
++  -V, --version    print version number and configuration settings, then exit
++  -q, --quiet, --silent
++                   do not print progress messages
++  -d, --debug      don't remove temporary files
++      --recheck    update $as_me by reconfiguring in the same conditions
++      --file=FILE[:TEMPLATE]
++                   instantiate the configuration file FILE
++      --header=FILE[:TEMPLATE]
++                   instantiate the configuration header FILE
++
++Configuration files:
++$config_files
++
++Configuration headers:
++$config_headers
++
++Report bugs to <bug-autoconf@gnu.org>."
++
++_ACEOF
++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
++ac_cs_version="\\
++open-isns config.status 0.90
++configured by $0, generated by GNU Autoconf 2.63,
++  with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
++
++Copyright (C) 2008 Free Software Foundation, Inc.
++This config.status script is free software; the Free Software Foundation
++gives unlimited permission to copy, distribute and modify it."
++
++ac_pwd='$ac_pwd'
++srcdir='$srcdir'
++INSTALL='$INSTALL'
++test -n "\$AWK" || AWK=awk
++_ACEOF
++
++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
++# The default lists apply if the user does not specify any file.
++ac_need_defaults=:
++while test $# != 0
++do
++  case $1 in
++  --*=*)
++    ac_option=`expr "X$1" : 'X\([^=]*\)='`
++    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
++    ac_shift=:
++    ;;
++  *)
++    ac_option=$1
++    ac_optarg=$2
++    ac_shift=shift
++    ;;
++  esac
++
++  case $ac_option in
++  # Handling of the options.
++  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
++    ac_cs_recheck=: ;;
++  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
++    $as_echo "$ac_cs_version"; exit ;;
++  --debug | --debu | --deb | --de | --d | -d )
++    debug=: ;;
++  --file | --fil | --fi | --f )
++    $ac_shift
++    case $ac_optarg in
++    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
++    esac
++    CONFIG_FILES="$CONFIG_FILES '$ac_optarg'"
++    ac_need_defaults=false;;
++  --header | --heade | --head | --hea )
++    $ac_shift
++    case $ac_optarg in
++    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
++    esac
++    CONFIG_HEADERS="$CONFIG_HEADERS '$ac_optarg'"
++    ac_need_defaults=false;;
++  --he | --h)
++    # Conflict between --help and --header
++    { $as_echo "$as_me: error: ambiguous option: $1
++Try \`$0 --help' for more information." >&2
++   { (exit 1); exit 1; }; };;
++  --help | --hel | -h )
++    $as_echo "$ac_cs_usage"; exit ;;
++  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
++  | -silent | --silent | --silen | --sile | --sil | --si | --s)
++    ac_cs_silent=: ;;
++
++  # This is an error.
++  -*) { $as_echo "$as_me: error: unrecognized option: $1
++Try \`$0 --help' for more information." >&2
++   { (exit 1); exit 1; }; } ;;
++
++  *) ac_config_targets="$ac_config_targets $1"
++     ac_need_defaults=false ;;
++
++  esac
++  shift
++done
++
++ac_configure_extra_args=
++
++if $ac_cs_silent; then
++  exec 6>/dev/null
++  ac_configure_extra_args="$ac_configure_extra_args --silent"
++fi
++
++_ACEOF
++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
++if \$ac_cs_recheck; then
++  set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
++  shift
++  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
++  CONFIG_SHELL='$SHELL'
++  export CONFIG_SHELL
++  exec "\$@"
++fi
++
++_ACEOF
++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
++exec 5>>config.log
++{
++  echo
++  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
++## Running $as_me. ##
++_ASBOX
++  $as_echo "$ac_log"
++} >&5
++
++_ACEOF
++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
++_ACEOF
++
++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
++
++# Handling of arguments.
++for ac_config_target in $ac_config_targets
++do
++  case $ac_config_target in
++    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
++    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
++
++  *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
++$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
++   { (exit 1); exit 1; }; };;
++  esac
++done
++
++
++# If the user did not use the arguments to specify the items to instantiate,
++# then the envvar interface is used.  Set only those that are not.
++# We use the long form for the default assignment because of an extremely
++# bizarre bug on SunOS 4.1.3.
++if $ac_need_defaults; then
++  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
++  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
++fi
++
++# Have a temporary directory for convenience.  Make it in the build tree
++# simply because there is no reason against having it here, and in addition,
++# creating and moving files from /tmp can sometimes cause problems.
++# Hook for its removal unless debugging.
++# Note that there is a small window in which the directory will not be cleaned:
++# after its creation but before its name has been assigned to `$tmp'.
++$debug ||
++{
++  tmp=
++  trap 'exit_status=$?
++  { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
++' 0
++  trap '{ (exit 1); exit 1; }' 1 2 13 15
++}
++# Create a (secure) tmp directory for tmp files.
++
++{
++  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
++  test -n "$tmp" && test -d "$tmp"
++}  ||
++{
++  tmp=./conf$$-$RANDOM
++  (umask 077 && mkdir "$tmp")
++} ||
++{
++   $as_echo "$as_me: cannot create a temporary directory in ." >&2
++   { (exit 1); exit 1; }
++}
++
++# Set up the scripts for CONFIG_FILES section.
++# No need to generate them if there are no CONFIG_FILES.
++# This happens for instance with `./config.status config.h'.
++if test -n "$CONFIG_FILES"; then
++
++
++ac_cr='
'
++ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
++if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
++  ac_cs_awk_cr='\\r'
++else
++  ac_cs_awk_cr=$ac_cr
++fi
++
++echo 'BEGIN {' >"$tmp/subs1.awk" &&
++_ACEOF
++
++
++{
++  echo "cat >conf$$subs.awk <<_ACEOF" &&
++  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
++  echo "_ACEOF"
++} >conf$$subs.sh ||
++  { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
++$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
++   { (exit 1); exit 1; }; }
++ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'`
++ac_delim='%!_!# '
++for ac_last_try in false false false false false :; do
++  . ./conf$$subs.sh ||
++    { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
++$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
++   { (exit 1); exit 1; }; }
++
++  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
++  if test $ac_delim_n = $ac_delim_num; then
++    break
++  elif $ac_last_try; then
++    { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
++$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
++   { (exit 1); exit 1; }; }
++  else
++    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
++  fi
++done
++rm -f conf$$subs.sh
++
++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
++cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
++_ACEOF
++sed -n '
++h
++s/^/S["/; s/!.*/"]=/
++p
++g
++s/^[^!]*!//
++:repl
++t repl
++s/'"$ac_delim"'$//
++t delim
++:nl
++h
++s/\(.\{148\}\).*/\1/
++t more1
++s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
++p
++n
++b repl
++:more1
++s/["\\]/\\&/g; s/^/"/; s/$/"\\/
++p
++g
++s/.\{148\}//
++t nl
++:delim
++h
++s/\(.\{148\}\).*/\1/
++t more2
++s/["\\]/\\&/g; s/^/"/; s/$/"/
++p
++b
++:more2
++s/["\\]/\\&/g; s/^/"/; s/$/"\\/
++p
++g
++s/.\{148\}//
++t delim
++' <conf$$subs.awk | sed '
++/^[^""]/{
++  N
++  s/\n//
++}
++' >>$CONFIG_STATUS || ac_write_fail=1
++rm -f conf$$subs.awk
++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
++_ACAWK
++cat >>"\$tmp/subs1.awk" <<_ACAWK &&
++  for (key in S) S_is_set[key] = 1
++  FS = ""
++
++}
++{
++  line = $ 0
++  nfields = split(line, field, "@")
++  substed = 0
++  len = length(field[1])
++  for (i = 2; i < nfields; i++) {
++    key = field[i]
++    keylen = length(key)
++    if (S_is_set[key]) {
++      value = S[key]
++      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
++      len += length(value) + length(field[++i])
++      substed = 1
++    } else
++      len += 1 + keylen
++  }
++
++  print line
++}
++
++_ACAWK
++_ACEOF
++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
++if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
++  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
++else
++  cat
++fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
++  || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5
++$as_echo "$as_me: error: could not setup config files machinery" >&2;}
++   { (exit 1); exit 1; }; }
++_ACEOF
++
++# VPATH may cause trouble with some makes, so we remove $(srcdir),
++# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
++# trailing colons and then remove the whole line if VPATH becomes empty
++# (actually we leave an empty line to preserve line numbers).
++if test "x$srcdir" = x.; then
++  ac_vpsub='/^[	 ]*VPATH[	 ]*=/{
++s/:*\$(srcdir):*/:/
++s/:*\${srcdir}:*/:/
++s/:*@srcdir@:*/:/
++s/^\([^=]*=[	 ]*\):*/\1/
++s/:*$//
++s/^[^=]*=[	 ]*$//
++}'
++fi
++
++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
++fi # test -n "$CONFIG_FILES"
++
++# Set up the scripts for CONFIG_HEADERS section.
++# No need to generate them if there are no CONFIG_HEADERS.
++# This happens for instance with `./config.status Makefile'.
++if test -n "$CONFIG_HEADERS"; then
++cat >"$tmp/defines.awk" <<\_ACAWK ||
++BEGIN {
++_ACEOF
++
++# Transform confdefs.h into an awk script `defines.awk', embedded as
++# here-document in config.status, that substitutes the proper values into
++# config.h.in to produce config.h.
++
++# Create a delimiter string that does not exist in confdefs.h, to ease
++# handling of long lines.
++ac_delim='%!_!# '
++for ac_last_try in false false :; do
++  ac_t=`sed -n "/$ac_delim/p" confdefs.h`
++  if test -z "$ac_t"; then
++    break
++  elif $ac_last_try; then
++    { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_HEADERS" >&5
++$as_echo "$as_me: error: could not make $CONFIG_HEADERS" >&2;}
++   { (exit 1); exit 1; }; }
++  else
++    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
++  fi
++done
++
++# For the awk script, D is an array of macro values keyed by name,
++# likewise P contains macro parameters if any.  Preserve backslash
++# newline sequences.
++
++ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
++sed -n '
++s/.\{148\}/&'"$ac_delim"'/g
++t rset
++:rset
++s/^[	 ]*#[	 ]*define[	 ][	 ]*/ /
++t def
++d
++:def
++s/\\$//
++t bsnl
++s/["\\]/\\&/g
++s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
++D["\1"]=" \3"/p
++s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2"/p
++d
++:bsnl
++s/["\\]/\\&/g
++s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
++D["\1"]=" \3\\\\\\n"\\/p
++t cont
++s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
++t cont
++d
++:cont
++n
++s/.\{148\}/&'"$ac_delim"'/g
++t clear
++:clear
++s/\\$//
++t bsnlc
++s/["\\]/\\&/g; s/^/"/; s/$/"/p
++d
++:bsnlc
++s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
++b cont
++' <confdefs.h | sed '
++s/'"$ac_delim"'/"\\\
++"/g' >>$CONFIG_STATUS || ac_write_fail=1
++
++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
++  for (key in D) D_is_set[key] = 1
++  FS = ""
++}
++/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
++  line = \$ 0
++  split(line, arg, " ")
++  if (arg[1] == "#") {
++    defundef = arg[2]
++    mac1 = arg[3]
++  } else {
++    defundef = substr(arg[1], 2)
++    mac1 = arg[2]
++  }
++  split(mac1, mac2, "(") #)
++  macro = mac2[1]
++  prefix = substr(line, 1, index(line, defundef) - 1)
++  if (D_is_set[macro]) {
++    # Preserve the white space surrounding the "#".
++    print prefix "define", macro P[macro] D[macro]
++    next
++  } else {
++    # Replace #undef with comments.  This is necessary, for example,
++    # in the case of _POSIX_SOURCE, which is predefined and required
++    # on some systems where configure will not decide to define it.
++    if (defundef == "undef") {
++      print "/*", prefix defundef, macro, "*/"
++      next
++    }
++  }
++}
++{ print }
++_ACAWK
++_ACEOF
++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
++  { { $as_echo "$as_me:$LINENO: error: could not setup config headers machinery" >&5
++$as_echo "$as_me: error: could not setup config headers machinery" >&2;}
++   { (exit 1); exit 1; }; }
++fi # test -n "$CONFIG_HEADERS"
++
++
++eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
++shift
++for ac_tag
++do
++  case $ac_tag in
++  :[FHLC]) ac_mode=$ac_tag; continue;;
++  esac
++  case $ac_mode$ac_tag in
++  :[FHL]*:*);;
++  :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5
++$as_echo "$as_me: error: invalid tag $ac_tag" >&2;}
++   { (exit 1); exit 1; }; };;
++  :[FH]-) ac_tag=-:-;;
++  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
++  esac
++  ac_save_IFS=$IFS
++  IFS=:
++  set x $ac_tag
++  IFS=$ac_save_IFS
++  shift
++  ac_file=$1
++  shift
++
++  case $ac_mode in
++  :L) ac_source=$1;;
++  :[FH])
++    ac_file_inputs=
++    for ac_f
++    do
++      case $ac_f in
++      -) ac_f="$tmp/stdin";;
++      *) # Look for the file first in the build tree, then in the source tree
++	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
++	 # because $ac_f cannot contain `:'.
++	 test -f "$ac_f" ||
++	   case $ac_f in
++	   [\\/$]*) false;;
++	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
++	   esac ||
++	   { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
++$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;}
++   { (exit 1); exit 1; }; };;
++      esac
++      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
++      ac_file_inputs="$ac_file_inputs '$ac_f'"
++    done
++
++    # Let's still pretend it is `configure' which instantiates (i.e., don't
++    # use $as_me), people would be surprised to read:
++    #    /* config.h.  Generated by config.status.  */
++    configure_input='Generated from '`
++	  $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
++	`' by configure.'
++    if test x"$ac_file" != x-; then
++      configure_input="$ac_file.  $configure_input"
++      { $as_echo "$as_me:$LINENO: creating $ac_file" >&5
++$as_echo "$as_me: creating $ac_file" >&6;}
++    fi
++    # Neutralize special characters interpreted by sed in replacement strings.
++    case $configure_input in #(
++    *\&* | *\|* | *\\* )
++       ac_sed_conf_input=`$as_echo "$configure_input" |
++       sed 's/[\\\\&|]/\\\\&/g'`;; #(
++    *) ac_sed_conf_input=$configure_input;;
++    esac
++
++    case $ac_tag in
++    *:-:* | *:-) cat >"$tmp/stdin" \
++      || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
++$as_echo "$as_me: error: could not create $ac_file" >&2;}
++   { (exit 1); exit 1; }; } ;;
++    esac
++    ;;
++  esac
++
++  ac_dir=`$as_dirname -- "$ac_file" ||
++$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
++	 X"$ac_file" : 'X\(//\)[^/]' \| \
++	 X"$ac_file" : 'X\(//\)$' \| \
++	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
++$as_echo X"$ac_file" |
++    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
++	    s//\1/
++	    q
++	  }
++	  /^X\(\/\/\)[^/].*/{
++	    s//\1/
++	    q
++	  }
++	  /^X\(\/\/\)$/{
++	    s//\1/
++	    q
++	  }
++	  /^X\(\/\).*/{
++	    s//\1/
++	    q
++	  }
++	  s/.*/./; q'`
++  { as_dir="$ac_dir"
++  case $as_dir in #(
++  -*) as_dir=./$as_dir;;
++  esac
++  test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
++    as_dirs=
++    while :; do
++      case $as_dir in #(
++      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
++      *) as_qdir=$as_dir;;
++      esac
++      as_dirs="'$as_qdir' $as_dirs"
++      as_dir=`$as_dirname -- "$as_dir" ||
++$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
++	 X"$as_dir" : 'X\(//\)[^/]' \| \
++	 X"$as_dir" : 'X\(//\)$' \| \
++	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
++$as_echo X"$as_dir" |
++    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
++	    s//\1/
++	    q
++	  }
++	  /^X\(\/\/\)[^/].*/{
++	    s//\1/
++	    q
++	  }
++	  /^X\(\/\/\)$/{
++	    s//\1/
++	    q
++	  }
++	  /^X\(\/\).*/{
++	    s//\1/
++	    q
++	  }
++	  s/.*/./; q'`
++      test -d "$as_dir" && break
++    done
++    test -z "$as_dirs" || eval "mkdir $as_dirs"
++  } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
++$as_echo "$as_me: error: cannot create directory $as_dir" >&2;}
++   { (exit 1); exit 1; }; }; }
++  ac_builddir=.
++
++case "$ac_dir" in
++.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
++*)
++  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
++  # A ".." for each directory in $ac_dir_suffix.
++  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
++  case $ac_top_builddir_sub in
++  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
++  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
++  esac ;;
++esac
++ac_abs_top_builddir=$ac_pwd
++ac_abs_builddir=$ac_pwd$ac_dir_suffix
++# for backward compatibility:
++ac_top_builddir=$ac_top_build_prefix
++
++case $srcdir in
++  .)  # We are building in place.
++    ac_srcdir=.
++    ac_top_srcdir=$ac_top_builddir_sub
++    ac_abs_top_srcdir=$ac_pwd ;;
++  [\\/]* | ?:[\\/]* )  # Absolute name.
++    ac_srcdir=$srcdir$ac_dir_suffix;
++    ac_top_srcdir=$srcdir
++    ac_abs_top_srcdir=$srcdir ;;
++  *) # Relative name.
++    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
++    ac_top_srcdir=$ac_top_build_prefix$srcdir
++    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
++esac
++ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
++
++
++  case $ac_mode in
++  :F)
++  #
++  # CONFIG_FILE
++  #
++
++  case $INSTALL in
++  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
++  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
++  esac
++_ACEOF
++
++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
++# If the template does not know about datarootdir, expand it.
++# FIXME: This hack should be removed a few years after 2.60.
++ac_datarootdir_hack=; ac_datarootdir_seen=
++
++ac_sed_dataroot='
++/datarootdir/ {
++  p
++  q
++}
++/@datadir@/p
++/@docdir@/p
++/@infodir@/p
++/@localedir@/p
++/@mandir@/p
++'
++case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
++*datarootdir*) ac_datarootdir_seen=yes;;
++*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
++  { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
++$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
++_ACEOF
++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
++  ac_datarootdir_hack='
++  s&@datadir@&$datadir&g
++  s&@docdir@&$docdir&g
++  s&@infodir@&$infodir&g
++  s&@localedir@&$localedir&g
++  s&@mandir@&$mandir&g
++    s&\\\${datarootdir}&$datarootdir&g' ;;
++esac
++_ACEOF
++
++# Neutralize VPATH when `$srcdir' = `.'.
++# Shell code in configure.ac might set extrasub.
++# FIXME: do we really want to maintain this feature?
++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
++ac_sed_extra="$ac_vpsub
++$extrasub
++_ACEOF
++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
++:t
++/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
++s|@configure_input@|$ac_sed_conf_input|;t t
++s&@top_builddir@&$ac_top_builddir_sub&;t t
++s&@top_build_prefix@&$ac_top_build_prefix&;t t
++s&@srcdir@&$ac_srcdir&;t t
++s&@abs_srcdir@&$ac_abs_srcdir&;t t
++s&@top_srcdir@&$ac_top_srcdir&;t t
++s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
++s&@builddir@&$ac_builddir&;t t
++s&@abs_builddir@&$ac_abs_builddir&;t t
++s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
++s&@INSTALL@&$ac_INSTALL&;t t
++$ac_datarootdir_hack
++"
++eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
++  || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
++$as_echo "$as_me: error: could not create $ac_file" >&2;}
++   { (exit 1); exit 1; }; }
++
++test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
++  { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
++  { ac_out=`sed -n '/^[	 ]*datarootdir[	 ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
++  { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'
++which seems to be undefined.  Please make sure it is defined." >&5
++$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
++which seems to be undefined.  Please make sure it is defined." >&2;}
++
++  rm -f "$tmp/stdin"
++  case $ac_file in
++  -) cat "$tmp/out" && rm -f "$tmp/out";;
++  *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
++  esac \
++  || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
++$as_echo "$as_me: error: could not create $ac_file" >&2;}
++   { (exit 1); exit 1; }; }
++ ;;
++  :H)
++  #
++  # CONFIG_HEADER
++  #
++  if test x"$ac_file" != x-; then
++    {
++      $as_echo "/* $configure_input  */" \
++      && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs"
++    } >"$tmp/config.h" \
++      || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
++$as_echo "$as_me: error: could not create $ac_file" >&2;}
++   { (exit 1); exit 1; }; }
++    if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then
++      { $as_echo "$as_me:$LINENO: $ac_file is unchanged" >&5
++$as_echo "$as_me: $ac_file is unchanged" >&6;}
++    else
++      rm -f "$ac_file"
++      mv "$tmp/config.h" "$ac_file" \
++	|| { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
++$as_echo "$as_me: error: could not create $ac_file" >&2;}
++   { (exit 1); exit 1; }; }
++    fi
++  else
++    $as_echo "/* $configure_input  */" \
++      && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \
++      || { { $as_echo "$as_me:$LINENO: error: could not create -" >&5
++$as_echo "$as_me: error: could not create -" >&2;}
++   { (exit 1); exit 1; }; }
++  fi
++ ;;
++
++
++  esac
++
++done # for ac_tag
++
++
++{ (exit 0); exit 0; }
++_ACEOF
++chmod +x $CONFIG_STATUS
++ac_clean_files=$ac_clean_files_save
++
++test $ac_write_fail = 0 ||
++  { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5
++$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;}
++   { (exit 1); exit 1; }; }
++
++
++# configure is writing to config.log, and then calls config.status.
++# config.status does its own redirection, appending to config.log.
++# Unfortunately, on DOS this fails, as config.log is still kept open
++# by configure, so config.status won't be able to write to it; its
++# output is simply discarded.  So we exec the FD to /dev/null,
++# effectively closing config.log, so it can be properly (re)opened and
++# appended to by config.status.  When coming back to configure, we
++# need to make the FD available again.
++if test "$no_create" != yes; then
++  ac_cs_success=:
++  ac_config_status_args=
++  test "$silent" = yes &&
++    ac_config_status_args="$ac_config_status_args --quiet"
++  exec 5>/dev/null
++  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
++  exec 5>>config.log
++  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
++  # would make configure fail if this is the last instruction.
++  $ac_cs_success || { (exit 1); exit 1; }
++fi
++if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
++  { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
++$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
++fi
++
+diff --git a/utils/open-isns/configure.ac b/utils/open-isns/configure.ac
+new file mode 100644
+index 0000000..a02915b
+--- /dev/null
++++ b/utils/open-isns/configure.ac
+@@ -0,0 +1,118 @@
++AC_INIT(open-isns, [0.90])
++AC_CONFIG_SRCDIR([isnsd.c])
++AC_CONFIG_AUX_DIR([aclocal])
++
++AC_CONFIG_HEADER(config.h)
++
++AC_PROG_CC
++AC_CANONICAL_HOST
++AC_C_BIGENDIAN
++
++AC_PROG_CPP
++AC_PROG_INSTALL
++AC_PROG_LN_S
++AC_PROG_MAKE_SET
++AC_PATH_PROG(SH, sh)
++
++dnl C Compiler features
++AC_C_INLINE
++if test "$GCC" = "yes"; then
++        CFLAGS="-Wall -fno-strict-aliasing $CFLAGS"
++	CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
++fi
++
++dnl Checks for header files.
++AC_HEADER_STDC
++AC_HEADER_SYS_WAIT
++AC_CHECK_HEADERS([errno.h fcntl.h malloc.h stdlib.h string.h strings.h sys/time.h unistd.h locale.h getopt.h])
++
++# Check if socket() is in libsocket
++AC_CHECK_LIB(socket, socket, [LIBS="$LIBS -lsocket"])
++
++
++AC_SUBST(GETOPTSRC)
++AC_CHECK_FUNC(getopt_long, AC_DEFINE(HAVE_GETOPT_LONG, 1, [Define if you have the `getopt_long' function.]),
++		[GETOPTSRC="$GETOPTSRC \$(top_srcdir)/compat/my_getopt.c"
++		CPPFLAGS="-I\$(top_srcdir)/compat/ $CPPFLAGS"
++		AC_DEFINE(HAVE_GETOPT_H, 1, [Define if you have the <getopt.h> header file.])])
++
++WITH_SECURITY=maybe
++AC_ARG_WITH(security,
++	[  --with-security         Enable iSNS authentication - requires OpenSSL],
++	[
++		if test "x$withval" = "xno" -o "x$withval" = "xyes"; then
++			WITH_SECURITY=$withval
++		else
++			WITH_SECURITY=yes
++			CPPFLAGS="$CPPFLAGS -I${withval}"
++			LDFLAGS="$LDFLAGS -L${withval}"
++		fi
++	]
++)
++
++if test "x$WITH_SECURITY" != "xno" ; then
++	# Check for openssl support - very primitive, we just
++	# check for the presence of crypto.h
++	AC_CHECK_HEADERS([openssl/crypto.h],
++		,
++		[have_libcrypto=no])
++	AC_CHECK_LIB(crypto, EVP_PKEY_new,
++		[SECLIBS="-lcrypto"],
++		[have_libcrypto=no])
++
++	if test "x$have_libcrypto" != "xno" ; then
++		AC_DEFINE(WITH_SECURITY, 1,
++			[Define if you want to support iSNS authentication])
++	else
++		if test "x$WITH_SECURITY" = "xyes" ; then
++			AC_MSG_ERROR([Security requested, but unable to find libcrypto])
++		fi
++	fi
++fi
++AC_SUBST(SECLIBS)
++
++WITH_SLP=maybe
++AC_ARG_WITH(slp,
++	[  --with-slp              Enable SLP for server discovery - requires OpenSLP],
++	[
++		if test "x$withval" = "xno" -o "x$withval" = "xyes"; then
++			WITH_SLP=$withval
++		else
++			WITH_SLP=yes
++			CPPFLAGS="$CPPFLAGS -I${withval}"
++			LDFLAGS="$LDFLAGS -L${withval}"
++		fi
++	]
++)
++
++if test "x$WITH_SLP" != "xno" ; then
++	# Check for openslp support - very primitive
++	AC_CHECK_HEADERS([slp.h],,
++		[have_openslp=no])
++	AC_CHECK_LIB(slp, SLPOpen,
++		[SLPLIBS="-lslp"],
++		[have_openslp=no])
++
++	if test "x$have_openslp" != "xno" ; then
++		AC_DEFINE(WITH_SLP, 1,
++			[Define if you want to support SLP discovery])
++	else
++		if test "x$WITH_SLP" = "xyes" ; then
++			AC_MSG_ERROR([SLP requested, but unable to find openslp])
++		fi
++	fi
++fi
++AC_SUBST(SLPLIBS)
++
++MEMDEBUG=
++AC_ARG_ENABLE(memdebug,
++	[  --enable-memdebug       Enable malloc debugging],
++	[
++		if test "x$enableval" = "xyes" ; then
++			CPPFLAGS="$CPPFLAGS -DMEMDEBUG"
++		fi
++	]
++)
++AC_SUBST(OPTIMIZE)
++
++AC_OUTPUT(Makefile)
+diff --git a/utils/open-isns/db-file.c b/utils/open-isns/db-file.c
+new file mode 100644
+index 0000000..98c08db
+--- /dev/null
++++ b/utils/open-isns/db-file.c
+@@ -0,0 +1,615 @@
++/*
++ * iSNS object database
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <sys/stat.h>
++#include <dirent.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <errno.h>
++#include <limits.h>
++
++#include "isns.h"
++#include "objects.h"
++#include "message.h"
++#include "util.h"
++#include "db.h"
++
++#define DBE_FILE_VERSION	1
++
++struct isns_db_file_info {
++	uint32_t	db_version;
++	uint32_t	db_last_eid;
++	uint32_t	db_last_index;
++};
++
++struct isns_db_object_info {
++	uint32_t	db_version;
++	char		db_type[64];
++	uint32_t	db_parent;
++	uint32_t	db_state;
++	uint32_t	db_flags;
++	uint32_t	db_scn_mask;
++	/* reserved bytes */
++	uint32_t	__db_reserved[15];
++};
++
++static int	isns_dbe_file_sync(isns_db_t *);
++static int	isns_dbe_file_reload(isns_db_t *);
++static int	isns_dbe_file_store(isns_db_t *,
++				const isns_object_t *);
++static int	isns_dbe_file_remove(isns_db_t *,
++				const isns_object_t *);
++static int	__dbe_file_load_all(const char *,
++				isns_object_list_t *);
++
++/*
++ * Helper functions
++ */
++static const char *
++__path_concat(const char *dirname, const char *basename)
++{
++	static char	pathname[PATH_MAX];
++
++	snprintf(pathname, sizeof(pathname), "%s/%s",
++			dirname, basename);
++	return pathname;
++}
++
++static const char *
++__print_index(uint32_t index)
++{
++	static char	namebuf[32];
++
++	snprintf(namebuf, sizeof(namebuf), "%08x", index);
++	return namebuf;
++}
++
++static int
++__get_index(const char *name, uint32_t *result)
++{
++	char	*end;
++
++	*result = strtoul(name, &end, 16);
++	if (*end)
++		return ISNS_INTERNAL_ERROR;
++	return ISNS_SUCCESS;
++}
++
++/*
++ * Build path names for an object
++ */
++static const char *
++__dbe_file_object_path(const char *dirname, const isns_object_t *obj)
++{
++	return __path_concat(dirname, __print_index(obj->ie_index));
++}
++
++/*
++ * Build a path name for a temporary file.
++ * Cannot use __path_concat, because we need both names
++ * when storing objects
++ */
++static const char *
++__dbe_file_object_temp(const char *dirname, const isns_object_t *obj)
++{
++	static char	pathname[PATH_MAX];
++
++	snprintf(pathname, sizeof(pathname), "%s/.%s",
++			dirname, __print_index(obj->ie_index));
++	return pathname;
++}
++
++/*
++ * Recursively create a directory
++ */
++static int
++__dbe_mkdir_path(const char *dirname)
++{
++	unsigned int true_len = strlen(dirname);
++	char	*copy, *s;
++
++	copy = isns_strdup(dirname);
++	
++	/* Walk up until we find a directory that exists */
++	while (1) {
++		s = strrchr(copy, '/');
++		if (s == NULL)
++			break;
++
++		*s = '\0';
++		if (access(copy, F_OK) == 0)
++			break;
++	}
++
++	while (strcmp(dirname, copy)) {
++		unsigned int len = strlen(copy);
++
++		/* Better safe than sorry */
++		isns_assert(len < true_len);
++
++		/* Put the next slash back in */
++		copy[len] = '/';
++
++		/* and try to create the directory */
++		if (mkdir(copy, 0700) < 0)
++			return -1;
++	}
++
++	return 0;
++}
++
++/*
++ * Write an object to a file
++ */
++static int
++__dbe_file_store_object(const char *dirname, const isns_object_t *obj)
++{
++	struct isns_db_object_info info;
++	const char	*path = __dbe_file_object_path(dirname, obj);
++	const char	*temp = __dbe_file_object_temp(dirname, obj);
++	buf_t		*bp = NULL;
++	int		status = ISNS_INTERNAL_ERROR;
++
++	isns_debug_state("DB: Storing object %u -> %s\n", obj->ie_index, path);
++	if (access(dirname, F_OK) < 0
++	 && (errno != ENOENT || __dbe_mkdir_path(dirname) < 0)) {
++		isns_error("DB: Unable to create %s: %m\n",
++				dirname);
++		goto out;
++	}
++
++	bp = buf_open(temp, O_CREAT|O_TRUNC|O_WRONLY);
++	if (bp == NULL) {
++		isns_error("Unable to open %s: %m\n", temp);
++		goto out;
++	}
++
++	/* Encode the header info ... */
++	memset(&info, 0, sizeof(info));
++	info.db_version = htonl(DBE_FILE_VERSION);
++	info.db_state = htonl(obj->ie_state);
++	info.db_flags = htonl(obj->ie_flags);
++	info.db_scn_mask = htonl(obj->ie_scn_mask);
++	strcpy(info.db_type, obj->ie_template->iot_name);
++	if (obj->ie_container)
++		info.db_parent = htonl(obj->ie_container->ie_index);
++
++	if (!buf_put(bp, &info, sizeof(info)))
++		goto out;
++
++	/* ... and attributes */
++	status = isns_attr_list_encode(bp, &obj->ie_attrs);
++	if (status != ISNS_SUCCESS)
++		goto out;
++
++	/* Renaming an open file. NFS will hate this */
++	if (rename(temp, path) < 0) {
++		isns_error("Cannot rename %s -> %s: %m\n",
++				temp, path);
++		unlink(temp);
++		status = ISNS_INTERNAL_ERROR;
++	}
++
++out:
++	if (bp)
++		buf_close(bp);
++	return status;
++}
++
++/*
++ * Store all children of an object
++ */
++static int
++__dbe_file_store_children(const char *dirname, const isns_object_t *obj)
++{
++	int		status = ISNS_SUCCESS;
++	unsigned int	i;
++
++	for (i = 0; i < obj->ie_children.iol_count; ++i) {
++		isns_object_t	*child;
++
++		child = obj->ie_children.iol_data[i];
++		status = __dbe_file_store_object(dirname, child);
++		if (status)
++			break;
++		status = __dbe_file_store_children(dirname, child);
++		if (status)
++			break;
++	}
++
++	return status;
++}
++
++/*
++ * Remove object and children
++ */
++static int
++__dbe_file_remove_object(const char *dirname, const isns_object_t *obj)
++{
++	const char	*path = __dbe_file_object_path(dirname, obj);
++
++	isns_debug_state("DB: Purging object %u (%s)\n", obj->ie_index, path);
++	if (unlink(path) < 0)
++		isns_error("DB: Cannot remove %s: %m\n", path);
++	return ISNS_SUCCESS;
++}
++
++static int
++__dbe_file_remove_children(const char *dirname, const isns_object_t *obj)
++{
++	const isns_object_list_t *list = &obj->ie_children;
++	unsigned int	i;
++
++	for (i = 0; i < list->iol_count; ++i)
++		__dbe_file_remove_object(dirname, list->iol_data[i]);
++
++	return ISNS_SUCCESS;
++}
++
++/*
++ * Load an object from file
++ */
++static int
++__dbe_file_load_object(const char *filename, const char *basename,
++		isns_object_list_t *result)
++{
++	struct isns_db_object_info info;
++	isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
++	isns_object_template_t *tmpl;
++	isns_object_t	*obj = NULL;
++	buf_t		*bp = NULL;
++	uint32_t	index;
++	int		status;
++
++	bp = buf_open(filename, O_RDONLY);
++	if (bp == NULL) {
++		isns_error("Unable to open %s: %m\n", filename);
++		goto internal_error;
++	}
++
++	/* Decode the header ... */
++	if (!buf_get(bp, &info, sizeof(info)))
++		goto internal_error;
++	if (info.db_version != htonl(DBE_FILE_VERSION)) {
++		/* If we ever have to deal with a DB version
++		 * upgrade, we could do it here. */
++		isns_fatal("Found iSNS database version %u; not supported\n",
++				ntohl(info.db_version));
++	}
++
++	/* ... and attributes */
++	status = isns_attr_list_decode(bp, &attrs);
++	if (status != ISNS_SUCCESS)
++		goto out;
++
++	/* Get the index from the file name */
++	status = __get_index(basename, &index);
++	if (status != ISNS_SUCCESS)
++		goto out;
++
++	tmpl = isns_object_template_by_name(info.db_type);
++	if (tmpl == NULL) {
++		isns_error("DB: Bad type name \"%s\" in object file\n",
++				info.db_type);
++		goto internal_error;
++	}
++
++	obj = isns_create_object(tmpl, &attrs, NULL);
++	if (obj == NULL)
++		goto internal_error;
++
++	obj->ie_state = ntohl(info.db_state);
++	obj->ie_flags = ntohl(info.db_flags) & ~(ISNS_OBJECT_DIRTY);
++	obj->ie_scn_mask = ntohl(info.db_scn_mask);
++	obj->ie_index = index;
++
++	/* Stash away the parent's index; we resolve them later on
++	 * once we've loaded all objects */
++	obj->ie_container_idx = ntohl(info.db_parent);
++
++	isns_object_list_append(result, obj);
++
++out:
++	if (bp)
++		buf_close(bp);
++	if (obj)
++		isns_object_release(obj);
++	isns_attr_list_destroy(&attrs);
++	return status;
++
++internal_error:
++	isns_error("Unable to load %s: Internal error\n",
++			filename);
++	status = ISNS_INTERNAL_ERROR;
++	goto out;
++}
++
++/*
++ * Load contents of directory into our database.
++ *
++ * We take two passes over the directory. In the first pass, we load
++ * all regular files containing objects. The file names correspond to
++ * the DB index.
++ *
++ * In the second pass, we load all directories, containing children of
++ * an object. The directories names are formed by the object's index,
++ * with ".d" appended to it.
++ */
++static int
++__dbe_file_load_all(const char *dirpath, isns_object_list_t *result)
++{
++	struct dirent *dp;
++	DIR	*dir;
++	int	status = ISNS_SUCCESS;
++
++	if ((dir = opendir(dirpath)) == NULL) {
++		isns_error("DB: cannot open %s: %m\n", dirpath);
++		return ISNS_INTERNAL_ERROR;
++	}
++
++	while ((dp = readdir(dir)) != NULL) {
++		struct stat	stb;
++		const char	*path;
++
++		if (dp->d_name[0] == '.'
++		 || !strcmp(dp->d_name, "DB"))
++			continue;
++
++		path = __path_concat(dirpath, dp->d_name);
++		if (lstat(path, &stb) < 0) {
++			isns_error("DB: cannot stat %s: %m\n", path);
++			status = ISNS_INTERNAL_ERROR;
++		} else
++		if (S_ISREG(stb.st_mode)) {
++			status = __dbe_file_load_object(path,
++					dp->d_name, result);
++		} else {
++			isns_debug_state("DB: ignoring %s\n", path);
++		}
++
++		if (status != ISNS_SUCCESS)
++			break;
++	}
++
++	closedir(dir);
++	return status;
++}
++
++/*
++ * Load and store DB metadata
++ */
++static int
++__dbe_file_write_info(isns_db_t *db)
++{
++	isns_db_backend_t *back = db->id_backend;
++	const char	*path;
++	buf_t		*bp;
++	int		status = ISNS_INTERNAL_ERROR;
++
++	path = __path_concat(back->idb_name, "DB");
++	if ((bp = buf_open(path, O_CREAT|O_TRUNC|O_WRONLY)) == NULL) {
++		isns_error("Unable to write %s: %m\n", path);
++		goto out;
++	}
++
++	if (buf_put32(bp, DBE_FILE_VERSION)
++	 && buf_put32(bp, db->id_last_eid)
++	 && buf_put32(bp, db->id_last_index))
++		status = ISNS_SUCCESS;
++
++out:
++	if (bp)
++		buf_close(bp);
++	return status;
++}
++
++static int
++__dbe_file_load_info(isns_db_t *db)
++{
++	isns_db_backend_t *back = db->id_backend;
++	struct isns_db_file_info info;
++	const char	*path;
++	buf_t		*bp = NULL;
++	int		status;
++
++	path = __path_concat(back->idb_name, "DB");
++	if ((bp = buf_open(path, O_RDONLY)) == NULL) {
++		status = ISNS_NO_SUCH_ENTRY;
++		goto out;
++	}
++
++	status = ISNS_INTERNAL_ERROR;
++	if (!buf_get32(bp, &info.db_version))
++		goto out;
++
++	if (info.db_version != DBE_FILE_VERSION) {
++		isns_error("DB file from unsupported version %04x\n",
++				info.db_version);
++		goto out;
++	}
++
++	if (buf_get32(bp, &info.db_last_eid)
++	 && buf_get32(bp, &info.db_last_index)) {
++		db->id_last_eid = info.db_last_eid;
++		db->id_last_index = info.db_last_index;
++		status = ISNS_SUCCESS;
++	}
++
++out:
++	if (bp)
++		buf_close(bp);
++	return status;
++}
++
++/*
++ * Find object with the given index.
++ */
++static isns_object_t *
++__dbe_find_object(isns_object_list_t *list, uint32_t index)
++{
++	unsigned int	i;
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t	*obj = list->iol_data[i];
++
++		if (obj->ie_index == index)
++			return obj;
++	}
++	return NULL;
++}
++
++int
++isns_dbe_file_reload(isns_db_t *db)
++{
++	isns_db_backend_t *back = db->id_backend;
++	int		status;
++	unsigned int	i;
++
++	isns_debug_state("DB: loading all objects from %s\n",
++			back->idb_name);
++
++	if (access(back->idb_name, R_OK) < 0) {
++		if (errno == ENOENT) {
++			/* Empty database is okay */
++			return ISNS_NO_SUCH_ENTRY;
++		}
++		isns_error("Cannot open database %s: %m\n", back->idb_name);
++		return ISNS_INTERNAL_ERROR;
++	}
++
++	status = __dbe_file_load_info(db);
++	if (status)
++		return status;
++
++	status = __dbe_file_load_all(back->idb_name, db->id_objects);
++	if (status)
++		return status;
++
++	/* Resolve parent/child relationship for all nodes */
++	for (i = 0; i < db->id_objects->iol_count; ++i) {
++		isns_object_t	*obj = db->id_objects->iol_data[i];
++		uint32_t	index = obj->ie_container_idx;
++		isns_object_t	*parent;
++
++		if (index == 0)
++			continue;
++
++		obj->ie_container = NULL;
++
++		parent = __dbe_find_object(db->id_objects, index);
++		if (parent == NULL) {
++			isns_warning("DB: object %u references "
++					"unknown container %u\n",
++					obj->ie_index,
++					index);
++		} else {
++			isns_object_attach(obj, parent);
++		}
++	}
++
++	/* Add objects to the appropriate lists */
++	for (i = 0; i < db->id_objects->iol_count; ++i) {
++		isns_object_template_t *tmpl;
++		isns_object_t	*obj = db->id_objects->iol_data[i];
++
++		switch (obj->ie_state) {
++		case ISNS_OBJECT_STATE_MATURE:
++			isns_scope_add(db->id_global_scope, obj);
++			obj->ie_references++;
++
++			tmpl = obj->ie_template;
++			if (tmpl->iot_build_relation
++			 && !tmpl->iot_build_relation(db, obj, NULL))
++				isns_warning("DB: cannot build relation for "
++						"object %u\n",
++						obj->ie_index);
++
++			if (obj->ie_relation)
++				isns_relation_add(db->id_relations,
++						obj->ie_relation);
++
++			if (ISNS_IS_ENTITY(obj))
++				isns_esi_register(obj);
++			break;
++
++		case ISNS_OBJECT_STATE_LIMBO:
++			isns_object_list_append(&db->id_limbo, obj);
++			break;
++
++		default:
++			isns_error("Unexpected object state %d in object %u "
++				"loaded from %s\n",
++				obj->ie_state, obj->ie_index,
++				back->idb_name);
++		}
++
++		/* Clear the dirty flag, which will be set when the
++		   object is created. */
++		obj->ie_flags &= ~ISNS_OBJECT_DIRTY;
++	}
++
++	return ISNS_SUCCESS;
++}
++
++int
++isns_dbe_file_sync(isns_db_t *db)
++{
++	return __dbe_file_write_info(db);
++}
++
++int
++isns_dbe_file_store(isns_db_t *db, const isns_object_t *obj)
++{
++	isns_db_backend_t *back = db->id_backend;
++	int		status;
++
++	if (obj->ie_index == 0) {
++		isns_error("DB: Refusing to store object with index 0\n");
++		return ISNS_INTERNAL_ERROR;
++	}
++
++	status = __dbe_file_store_object(back->idb_name, obj);
++	if (status == ISNS_SUCCESS)
++		status = __dbe_file_store_children(back->idb_name, obj);
++
++	return status;
++}
++
++int
++isns_dbe_file_remove(isns_db_t *db, const isns_object_t *obj)
++{
++	isns_db_backend_t *back = db->id_backend;
++	int		status;
++
++	status = __dbe_file_remove_object(back->idb_name, obj);
++	if (status == ISNS_SUCCESS)
++		status = __dbe_file_remove_children(back->idb_name, obj);
++
++	return status;
++}
++
++/*
++ * Create the file backend
++ */
++isns_db_backend_t *
++isns_create_file_db_backend(const char *pathname)
++{
++	isns_db_backend_t *back;
++
++	isns_debug_state("Creating file DB backend (%s)\n", pathname);
++
++	back = isns_calloc(1, sizeof(*back));
++	back->idb_name = isns_strdup(pathname);
++	back->idb_reload = isns_dbe_file_reload;
++	back->idb_sync = isns_dbe_file_sync;
++	back->idb_store = isns_dbe_file_store;
++	back->idb_remove = isns_dbe_file_remove;
++
++	return back;
++}
++
+diff --git a/utils/open-isns/db-policy.c b/utils/open-isns/db-policy.c
+new file mode 100644
+index 0000000..a85a436
+--- /dev/null
++++ b/utils/open-isns/db-policy.c
+@@ -0,0 +1,187 @@
++/*
++ * Use database as policy and keystore
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <sys/stat.h>
++#include <string.h>
++#include <unistd.h>
++#ifdef WITH_SECURITY
++#include <openssl/pem.h>
++#include <openssl/err.h>
++#endif
++#include "isns.h"
++#include "security.h"
++#include "objects.h"
++#include "vendor.h"
++#include "util.h"
++#include "config.h"
++
++/*
++ * DB keystore
++ */
++typedef struct isns_db_keystore isns_db_keystore_t;
++struct isns_db_keystore {
++	isns_keystore_t		sd_base;
++	isns_db_t *		sd_db;
++	isns_object_t *		sd_control;
++};
++
++/*
++ * Look up the policy object given its SPI
++ */
++isns_object_t *
++__isns_db_keystore_lookup(isns_db_keystore_t *store,
++		const char *name, size_t namelen)
++{
++	isns_attr_list_t keys = ISNS_ATTR_LIST_INIT;
++	char		namebuf[256];
++
++	if (namelen >= sizeof(namebuf))
++		return NULL;
++	memcpy(namebuf, name, namelen);
++	namebuf[namelen] = '\0';
++
++	isns_attr_list_append_string(&keys,
++			OPENISNS_TAG_POLICY_SPI,
++			namebuf);
++	return isns_db_lookup(store->sd_db, NULL, &keys);
++}
++
++/*
++ * Load a DSA key from the DB store
++ */
++static EVP_PKEY *
++__isns_db_keystore_find(isns_keystore_t *store_base,
++		const char *name, size_t namelen)
++{
++#ifdef WITH_SECURITY
++	isns_db_keystore_t *store = (isns_db_keystore_t *) store_base;
++	isns_object_t	*obj;
++	const void	*key_data;
++	size_t		key_size;
++
++	obj = __isns_db_keystore_lookup(store, name, namelen);
++	if (obj == NULL)
++		return NULL;
++
++	if (!isns_object_get_opaque(obj, OPENISNS_TAG_POLICY_KEY,
++				&key_data, &key_size))
++		return NULL;
++
++	return isns_dsa_decode_public(key_data, key_size);
++#else
++	return NULL;
++#endif
++}
++
++/*
++ * Retrieve policy from database
++ */
++static void
++__isns_db_keystore_copy_policy_string(isns_object_t *obj,
++		uint32_t tag, char **var)
++{
++	const char	*value;
++
++	if (!isns_object_get_string(obj, tag, &value))
++		return;
++	isns_assign_string(var, value);
++}
++
++static void
++__isns_db_keystore_copy_policy_strings(isns_object_t *obj,
++		uint32_t tag, struct string_array *array)
++{
++	isns_attr_list_t *attrs = &obj->ie_attrs;
++	unsigned int	i;
++
++	for (i = 0; i < attrs->ial_count; ++i) {
++		isns_attr_t *attr = attrs->ial_data[i];
++
++		if (attr->ia_tag_id != tag
++		 || !ISNS_ATTR_IS_STRING(attr))
++			continue;
++		isns_string_array_append(array, attr->ia_value.iv_string);
++	}
++}
++
++static isns_policy_t *
++__isns_db_keystore_get_policy(isns_keystore_t *store_base,
++		     const char *name, size_t namelen)
++{
++	isns_db_keystore_t *store = (isns_db_keystore_t *) store_base;
++	isns_policy_t	*policy;
++	isns_object_t	*obj;
++	uint32_t	intval;
++
++	obj = __isns_db_keystore_lookup(store, name, namelen);
++	if (obj == NULL)
++		return NULL;
++
++	policy = __isns_policy_alloc(name, namelen);
++
++	/* retrieve policy bits from object */
++#if 0
++	__isns_db_keystore_copy_policy_string(obj,
++			OPENISNS_TAG_POLICY_SOURCE_NAME,
++			&policy->ip_source);
++#endif
++	__isns_db_keystore_copy_policy_string(obj,
++			OPENISNS_TAG_POLICY_ENTITY,
++			&policy->ip_entity);
++	__isns_db_keystore_copy_policy_string(obj,
++			OPENISNS_TAG_POLICY_DEFAULT_DD,
++			&policy->ip_dd_default);
++	__isns_db_keystore_copy_policy_strings(obj,
++			OPENISNS_TAG_POLICY_NODE_NAME,
++			&policy->ip_node_names);
++
++	if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_OBJECT_TYPE, &intval))
++		policy->ip_object_types = intval;
++	if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_NODE_TYPE, &intval))
++		policy->ip_node_types = intval;
++	if (isns_object_get_uint32(obj, OPENISNS_TAG_POLICY_FUNCTIONS, &intval))
++		policy->ip_functions = intval;
++
++	return policy;
++}
++
++void
++__isns_db_keystore_change_notify(const isns_db_event_t *ev, void *handle)
++{
++	isns_db_keystore_t *store = handle;
++	isns_object_t *obj = ev->ie_object;
++
++	if (isns_object_get_entity(obj) == store->sd_control) {
++		isns_debug_auth("DB keystore: policy data was modified\n");
++		store->sd_base.ic_generation++;
++	}
++}
++
++isns_keystore_t *
++isns_create_db_keystore(isns_db_t *db)
++{
++	isns_db_keystore_t *store;
++	isns_object_t	*entity;
++
++	isns_debug_auth("Creating DB keystore\n");
++	if (!(entity = isns_db_get_control(db))) {
++		isns_error("Could not create control entity in database\n");
++		return NULL;
++	}
++	isns_debug_auth("Control entity is 0x%08x\n", entity->ie_index);
++
++	store = isns_calloc(1, sizeof(*store));
++	store->sd_base.ic_name = "database key store";
++	store->sd_base.ic_find = __isns_db_keystore_find;
++	store->sd_base.ic_get_policy = __isns_db_keystore_get_policy;
++	store->sd_control = entity;
++	store->sd_db = db;
++
++	isns_register_callback(__isns_db_keystore_change_notify, store);
++
++	return (isns_keystore_t *) store;
++}
++
+diff --git a/utils/open-isns/db.c b/utils/open-isns/db.c
+new file mode 100644
+index 0000000..c66dfbb
+--- /dev/null
++++ b/utils/open-isns/db.c
+@@ -0,0 +1,994 @@
++/*
++ * iSNS object database
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include <stdarg.h>
++
++#include "isns.h"
++#include "objects.h"
++#include "db.h"
++#include "util.h"
++
++enum {
++	IDT_INSERT,
++	IDT_REMOVE,
++	IDT_UPDATE
++};
++struct isns_db_trans {
++	struct isns_db_trans *	idt_next;
++	int			idt_action;
++	isns_object_t *		idt_object;
++};
++
++/* Internal helpers */
++static int	isns_db_sanity_check(isns_db_t *);
++static int	isns_db_get_key_tags(const isns_attr_list_t *,
++			uint32_t *, unsigned int);
++static int	isns_db_keyed_compare(const isns_object_t *,
++			const isns_attr_list_t *,
++			const uint32_t *, unsigned int);
++
++/*
++ * Open a database
++ */
++static isns_db_t *
++isns_db_create(isns_db_backend_t *backend)
++{
++	isns_db_t *db;
++
++	db = isns_calloc(1, sizeof(*db));
++	db->id_last_index = 1;
++	db->id_last_eid = 1;
++	db->id_backend = backend;
++	db->id_global_scope = isns_scope_alloc(db);
++	db->id_relations = isns_relation_soup_alloc();
++	db->id_objects = &db->__id_objects;
++
++	if (backend && backend->idb_reload) {
++		int	status;
++
++		status = backend->idb_reload(db);
++		/* "No such entry" is returned when the DB
++		 * is still empty. */
++		if (status != ISNS_SUCCESS
++		 && status != ISNS_NO_SUCH_ENTRY) {
++			isns_error("Error loading database: %s\n",
++					isns_strerror(status));
++			/* FIXME: isns_db_free(db); */
++			return NULL;
++		}
++
++		isns_db_sanity_check(db);
++	}
++
++	return db;
++}
++
++isns_db_t *
++isns_db_open(const char *location)
++{
++	isns_db_backend_t *backend;
++
++	if (location == NULL) {
++		isns_debug_state("Using in-memory DB\n");
++		return isns_db_create(NULL);
++	}
++
++	if (location[0] == '/') {
++		backend = isns_create_file_db_backend(location);
++	} else
++	if (!strncmp(location, "file:", 5)) {
++		backend = isns_create_file_db_backend(location + 5);
++	} else {
++		isns_error("Unsupported database type \"%s\"\n",
++				location);
++		return NULL;
++	}
++
++	return isns_db_create(backend);
++}
++
++isns_db_t *
++isns_db_open_shadow(isns_object_list_t *list)
++{
++	isns_db_t	*db;
++
++	if ((db = isns_db_create(NULL)) != NULL)
++		db->id_objects = list;
++	return db;
++}
++
++int
++isns_db_sanity_check(isns_db_t *db)
++{
++	unsigned int	i;
++
++	i = 0;
++	while (i < db->id_objects->iol_count) {
++		isns_object_t *obj = db->id_objects->iol_data[i];
++
++		switch (obj->ie_state) {
++		case ISNS_OBJECT_STATE_MATURE:
++			/* Nothing yet. */
++			break;
++
++		case ISNS_OBJECT_STATE_LIMBO:
++			if (!ISNS_IS_ISCSI_NODE(obj)
++			 && !ISNS_IS_PORTAL(obj)) {
++				isns_error("Unexpected object %u (%s) in limbo\n",
++						obj->ie_index,
++						obj->ie_template->iot_name);
++				isns_db_remove(db, obj);
++			}
++			break;
++
++		default:
++			isns_error("Unexpected object state %d in object %u (%s)\n",
++				obj->ie_state, obj->ie_index,
++				obj->ie_template->iot_name);
++			isns_db_remove(db, obj);
++			break;
++		}
++
++		i += 1;
++	}
++
++	return 1;
++}
++
++isns_object_t *
++isns_db_lookup(isns_db_t *db,
++		isns_object_template_t *tmpl,
++		const isns_attr_list_t *keys)
++{
++	return isns_object_list_lookup(db->id_objects, tmpl, keys);
++}
++
++int
++isns_db_gang_lookup(isns_db_t *db,
++		isns_object_template_t *tmpl,
++		const isns_attr_list_t *keys,
++		isns_object_list_t *result)
++{
++	return isns_object_list_gang_lookup(db->id_objects,
++			tmpl, keys, result);
++}
++
++/*
++ * Look up the storage node for the given source.
++ */
++isns_object_t *
++isns_db_lookup_source_node(isns_db_t *db,
++		const isns_source_t *source)
++{
++	isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
++	isns_object_t	*node;
++
++	isns_attr_list_append_attr(&attrs, isns_source_attr(source));
++	node = isns_db_lookup(db, NULL, &attrs);
++	isns_attr_list_destroy(&attrs);
++
++	return node;
++}
++
++isns_object_t *
++isns_db_vlookup(isns_db_t *db,
++		isns_object_template_t *tmpl,
++		...)
++{
++	isns_attr_list_t keys = ISNS_ATTR_LIST_INIT;
++	isns_object_t *obj = NULL;
++	va_list	ap;
++
++	va_start(ap, tmpl);
++	while (1) {
++		const isns_tag_type_t *tag_type;
++		isns_value_t	value;
++		uint32_t	tag;
++
++		tag = va_arg(ap, unsigned int);
++		if (tag == 0)
++			break;
++
++		tag_type = isns_tag_type_by_id(tag);
++		if (tag_type == NULL) {
++			isns_error("isns_db_vlookup: unknown tag %u\n", tag);
++			goto out;
++		}
++
++		memset(&value, 0, sizeof(value));
++		value.iv_type = tag_type->it_type;
++		switch (tag_type->it_type->it_id) {
++		case ISNS_ATTR_TYPE_STRING:
++			value.iv_string = va_arg(ap, char *);
++			break;
++
++		case ISNS_ATTR_TYPE_INT32:
++			value.iv_int32 = va_arg(ap, int32_t);
++			break;
++
++		case ISNS_ATTR_TYPE_UINT32:
++			value.iv_int32 = va_arg(ap, uint32_t);
++			break;
++
++		case ISNS_ATTR_TYPE_IPADDR:
++			value.iv_ipaddr = *va_arg(ap, struct in6_addr *);
++			break;
++
++		default:
++			isns_error("isns_db_vlookup: unsupported tag type %s\n",
++					value.iv_type->it_name);
++			goto out;
++		}
++
++		isns_attr_list_append_value(&keys, tag, tag_type, &value);
++	}
++
++	obj = isns_db_lookup(db, tmpl, &keys);
++
++out:
++	isns_attr_list_destroy(&keys);
++	va_end(ap);
++	return obj;
++}
++
++/*
++ * Find the next matching object
++ *
++ * This implementation could be a lot simpler if the
++ * RFC didn't make things so awfully complicated.
++ * It could simply have mandated the use of the object
++ * index attribute, period.
++ */
++isns_object_t *
++__isns_db_get_next(const isns_object_list_t *list,
++		isns_object_template_t *tmpl,
++		const isns_attr_list_t *current,
++		const isns_attr_list_t *scope)
++{
++	isns_object_t	*next = NULL;
++	uint32_t	tags[16];
++	unsigned int	i;
++	int		num_tags;
++
++	if (!tmpl)
++		return NULL;
++
++	/* Get the search attribute tags, and sort them.
++	 * Note, these don't have to be the standard key
++	 * attributes for a given object type; the RFC
++	 * also permits index attributes.
++	 */
++	num_tags = isns_db_get_key_tags(current, tags, 16);
++	if (num_tags < 0)
++		return NULL;
++
++	/*
++	 * 5.6.5.3.
++	 * If the TLV length of the Message Key Attribute(s) is zero,
++	 * then the first object entry in the iSNS database matching the
++	 * Message Key type SHALL be returned in the Message Key of the
++	 * corresponding DevGetNextRsp message.
++	 */
++	for (i = 0; i < current->ial_count; ++i) {
++		isns_attr_t *attr = current->ial_data[i];
++
++		if (!ISNS_ATTR_IS_NIL(attr))
++			goto non_nil;
++	}
++	current = NULL;
++non_nil:
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t	*obj = list->iol_data[i];
++
++		if (obj->ie_template != tmpl)
++			continue;
++		if (scope && !isns_object_match(obj, scope))
++			continue;
++
++		/* compare returns -1 if the first list
++		 * is "before" the second list, in terms of
++		 * implicit ordering. */
++		if (current
++		 && isns_db_keyed_compare(obj, current, tags, num_tags) <= 0) {
++			/* obj less than or equal to current */
++			continue;
++		}
++
++		if (next == NULL
++		 || isns_db_keyed_compare(obj, &next->ie_attrs, tags, num_tags) < 0)
++			next = obj;
++	}
++
++	if (next)
++		isns_object_get(next);
++	return next;
++}
++
++isns_object_t *
++isns_db_get_next(isns_db_t *db,
++		isns_object_template_t *tmpl,
++		const isns_attr_list_t *current,
++		const isns_attr_list_t *scope,
++		const isns_source_t *source)
++{
++	return __isns_db_get_next(db->id_objects,
++			tmpl, current, scope);
++}
++
++/*
++ * Get the search key tags
++ */
++static int
++isns_db_get_key_tags(const isns_attr_list_t *keys,
++		uint32_t *tags, unsigned int max_tags)
++{
++	unsigned int	i;
++
++	/* Get the search attribute tags, and sort them */
++	for (i = 0; i < keys->ial_count; ++i) {
++		if (i >= 16)
++			return -1;
++		tags[i] = keys->ial_data[i]->ia_tag_id;
++	}
++
++	/* FIXME: qsort the list */
++	return i;
++}
++
++/*
++ * Helper function for GetNext
++ */
++static int
++isns_db_keyed_compare(const isns_object_t *obj,
++		const isns_attr_list_t *attrs,
++		const uint32_t *tags, unsigned int num_tags)
++{
++	int		ind = 0;
++	unsigned int	i;
++
++	for (i = 0; i < num_tags; ++i) {
++		isns_attr_t	*attr1, *attr2;
++		uint32_t	tag = tags[i];
++
++		if (!isns_attr_list_get_attr(&obj->ie_attrs, tag, &attr1))
++			attr1 = NULL;
++		if (!isns_attr_list_get_attr(attrs, tag, &attr2))
++			attr2 = NULL;
++		if (attr1 == attr2) {
++			ind = 0;
++		} else if (attr1 && attr2) {
++			ind = isns_attr_compare(attr1, attr2);
++		} else if (attr1 == NULL) {
++			ind = -1;
++		} else {
++			ind = 1;
++		}
++		if (ind)
++			break;
++	}
++	return ind;
++}
++
++uint32_t
++isns_db_allocate_index(isns_db_t *db)
++{
++	return db->id_last_index++;
++}
++
++/*
++ * Insert an object into the database.
++ */
++void
++__isns_db_insert(isns_db_t *db, isns_object_t *obj, unsigned int state)
++{
++	uint32_t	idx_tag = obj->ie_template->iot_index;
++
++	switch (obj->ie_state) {
++	case ISNS_OBJECT_STATE_LIMBO:
++		/* The object was in limbo; now it goes
++		 * live (again). It should have an index,
++		 * and it should be on the global id_objects
++		 * list too.
++		 */
++		isns_assert(state == ISNS_OBJECT_STATE_MATURE);
++		isns_assert(obj->ie_index);
++		isns_assert(obj->ie_users > 1);
++		isns_object_list_remove(&db->id_limbo, obj);
++		break;
++
++	case ISNS_OBJECT_STATE_DEAD:
++		/* A DevAttrReg with the F_REPLACE bit set will cause
++		 * the key object to be removed from the DB, which may
++		 * kill it for good.
++		 * The subsequent call to db_insert will assign a new
++		 * index, and re-add it to the database.
++		 */
++
++	case ISNS_OBJECT_STATE_LARVAL:
++		/* Larval objects can go either to mature or
++		 * limbo state. */
++		obj->ie_index = db->id_last_index++;
++
++		if (idx_tag)
++			isns_object_set_uint32(obj,
++				idx_tag,
++				obj->ie_index);
++
++		isns_object_list_append(db->id_objects, obj);
++		break;
++
++	case ISNS_OBJECT_STATE_MATURE:
++		/* If we call db_insert on a mature object, treat
++		   this as a NOP. */
++		isns_assert(state == ISNS_OBJECT_STATE_MATURE);
++		return;
++
++	default:
++		isns_error("Internal error: unexpected object %u (%s) "
++				"state %u in db_insert\n",
++				obj->ie_index,
++				obj->ie_template->iot_name,
++				obj->ie_state);
++		return;
++	}
++
++	obj->ie_state = state;
++
++	/* Add it to the global scope */
++	if (state == ISNS_OBJECT_STATE_MATURE) {
++		isns_scope_add(db->id_global_scope, obj);
++		obj->ie_references++;
++
++		/* See if this object represents a relationship
++		 * (eg a portal group). */
++		if (obj->ie_template->iot_relation_type) {
++			if (!obj->ie_relation) {
++				isns_warning("DB: inserting %s object "
++						"without relation\n",
++						obj->ie_template->iot_name);
++			} else {
++				isns_relation_add(db->id_relations,
++						obj->ie_relation);
++			}
++		}
++
++		isns_mark_object(obj, ISNS_SCN_OBJECT_ADDED);
++	}
++
++	isns_debug_state("DB: added object %u (%s) state %u\n",
++			obj->ie_index,
++			obj->ie_template->iot_name,
++			obj->ie_state);
++
++	if (db->id_backend) {
++		db->id_backend->idb_store(db, obj);
++		db->id_backend->idb_sync(db);
++	}
++}
++
++void
++isns_db_insert(isns_db_t *db, isns_object_t *obj)
++{
++	__isns_db_insert(db, obj, ISNS_OBJECT_STATE_MATURE);
++}
++
++void
++isns_db_insert_limbo(isns_db_t *db, isns_object_t *obj)
++{
++	isns_assert(obj->ie_state == ISNS_OBJECT_STATE_LARVAL);
++	__isns_db_insert(db, obj, ISNS_OBJECT_STATE_LIMBO);
++}
++
++/*
++ * Save an object after updating it
++ */
++void
++isns_db_sync(isns_db_t *db)
++{
++	isns_object_list_t *list = db->id_objects;
++	unsigned int	i, saved = 0;
++
++	if (!db->id_backend)
++		return;
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t	*obj = list->iol_data[i];
++
++		if (obj->ie_flags & ISNS_OBJECT_DIRTY) {
++			db->id_backend->idb_store(db, obj);
++			obj->ie_flags &= ~ISNS_OBJECT_DIRTY;
++			saved++;
++		}
++	}
++	if (saved)
++		db->id_backend->idb_sync(db);
++}
++
++/*
++ * Remove an object from the database.
++ * This is slow and inefficient, due to the use
++ * of an object array. We should at least use
++ * a linked list, or maybe even a hash one day.
++ */
++static void
++__isns_db_prepare_removal(isns_db_t *db, isns_object_t *obj)
++{
++	isns_object_t	*child;
++
++	obj->ie_flags |= ISNS_OBJECT_DEAD;
++	isns_object_get(obj);
++
++	/* The node is dead; it's no longer interested in SCNs */
++	obj->ie_scn_mask = 0;
++
++	/* Trigger an SCN event. */
++	if (obj->ie_state == ISNS_OBJECT_STATE_MATURE)
++		isns_mark_object(obj, ISNS_SCN_OBJECT_REMOVED);
++
++	/* If the object represents a relation between
++	 * two other objects, sever that relationship.
++	 */
++	if (obj->ie_relation) {
++		isns_relation_remove(db->id_relations,
++				obj->ie_relation);
++		isns_relation_sever(obj->ie_relation);
++		isns_relation_release(obj->ie_relation);
++		obj->ie_relation = NULL;
++	}
++
++	/* Detach the object from its container */
++	isns_object_detach(obj);
++
++	/* Remove it from the database */
++	if (isns_scope_remove(db->id_global_scope, obj)) {
++		obj->ie_references--;
++	} else {
++		isns_warning("Unable to remove object from scope\n");
++	}
++
++	/* Recursively remove all children */
++	while (obj->ie_children.iol_count) {
++		child = obj->ie_children.iol_data[0];
++		__isns_db_prepare_removal(db, child);
++	}
++
++	isns_debug_state("DB: removed object %u (%s)\n",
++			obj->ie_index,
++			obj->ie_template->iot_name);
++
++	isns_object_list_append(&db->id_deferred, obj);
++	isns_object_release(obj);
++}
++
++int
++isns_db_remove(isns_db_t *db, isns_object_t *obj)
++{
++	isns_object_t	*entity;
++	unsigned int	i;
++
++	/* Don't even bother if the object was never added */
++	if (obj->ie_index == 0)
++		goto out;
++
++	/* Obtain the containing entity before removal */
++	entity = isns_object_get_entity(obj);
++
++	/* We don't remove the object for real yet; 
++	 * this will happen later during db_purge */
++	__isns_db_prepare_removal(db, obj);
++
++	/*
++	 * 5.6.5.4.
++	 * If all Nodes and Portals associated with a Network Entity are
++	 * deregistered, then the Network Entity SHALL also be removed.
++	 *
++	 * If both the Portal and iSCSI Storage Node objects associated
++	 * with a Portal Group object are removed, then that Portal Group
++	 * object SHALL also be removed.  The Portal Group object SHALL
++	 * remain registered as long as either of its associated Portal
++	 * or iSCSI Storage Node objects remain registered.  If a deleted
++	 * Storage Node or Portal object is subsequently re-registered,
++	 * then a relationship between the re- registered object and
++	 * an existing Portal or Storage Node object registration,
++	 * indicated by the PG object, SHALL be restored.
++	 */
++	if (ISNS_IS_ENTITY(obj))
++		goto out;
++
++	if (entity == NULL || !ISNS_IS_ENTITY(entity))
++		goto out;
++
++	/* Don't do this for the CONTROL entity. */
++	if (entity->ie_flags & ISNS_OBJECT_PRIVATE)
++		goto out;
++
++	/* Step 1: Purge all relationship objects (read: portal groups)
++	 * where both referenced objects are dead.
++	 */
++	for (i = 0; i < entity->ie_children.iol_count; ) {
++		isns_object_t *child = entity->ie_children.iol_data[i];
++
++		if (child->ie_relation
++		 && isns_relation_is_dead(child->ie_relation)) {
++			__isns_db_prepare_removal(db, child);
++			continue;
++		}
++
++		i += 1;
++	}
++
++	/* Step 2: If all portals, nodes and PGs have been unregistered,
++	 * the list of children should be empty. */
++	if (entity->ie_children.iol_count == 0) {
++		isns_debug_state("Last portal/node unregistered, removing entity\n");
++		__isns_db_prepare_removal(db, entity);
++	}
++
++out:
++	return ISNS_SUCCESS;
++}
++
++/*
++ * Purge deregistered objects.
++ * If we find they're still part of some discovery
++ * domain, they're moved to id_limbo; otherwise we'll
++ * destroy them for good.
++ */
++void
++isns_db_purge(isns_db_t *db)
++{
++	isns_object_list_t *list = &db->id_deferred;
++	unsigned int	i;
++
++	while (list->iol_count) {
++		isns_object_t *obj = list->iol_data[0];
++
++		if (obj->ie_references == 0) {
++			isns_debug_state("DB: destroying object %u (%s)\n",
++					obj->ie_index,
++					obj->ie_template->iot_name);
++
++			if (db->id_backend) {
++				db->id_backend->idb_remove(db, obj);
++				/* db->id_backend->idb_sync(db); */
++			}
++
++			isns_object_list_remove(db->id_objects, obj);
++			obj->ie_state = ISNS_OBJECT_STATE_DEAD;
++		} else if (obj->ie_state != ISNS_OBJECT_STATE_LIMBO) {
++			isns_debug_state("DB: moving object %u (%s) to purgatory - "
++					"%u references left\n",
++					obj->ie_index,
++					obj->ie_template->iot_name,
++					obj->ie_references);
++
++			isns_object_list_append(&db->id_limbo, obj);
++			obj->ie_state = ISNS_OBJECT_STATE_LIMBO;
++			isns_object_prune_attrs(obj);
++
++			if (db->id_backend) {
++				db->id_backend->idb_store(db, obj);
++				db->id_backend->idb_sync(db);
++			}
++		}
++
++		isns_object_list_remove(list, obj);
++	}
++
++	/* Brute force - look at all objects in limbo and kill those
++	 * that went out of scope */
++	for (i = 0; i < db->id_limbo.iol_count; ) {
++		isns_object_t *obj = db->id_limbo.iol_data[i];
++
++		if (obj->ie_references == 0) {
++			isns_debug_state("DB: destroying object %u (%s)\n",
++					obj->ie_index,
++					obj->ie_template->iot_name);
++
++			if (db->id_backend) {
++				db->id_backend->idb_remove(db, obj);
++				/* db->id_backend->idb_sync(db); */
++			}
++
++			obj->ie_state = ISNS_OBJECT_STATE_DEAD;
++			isns_object_list_remove(&db->id_limbo, obj);
++			isns_object_list_remove(db->id_objects, obj);
++			continue;
++		}
++
++		i += 1;
++	}
++}
++
++/*
++ * Expire old entities
++ *
++ * This code is still rather simple, but once we start
++ * using ESI things get rather complex quickly.
++ */
++time_t
++isns_db_expire(isns_db_t *db)
++{
++	isns_object_list_t *list = db->id_objects;
++	time_t		now = time(NULL), next_timeout;
++	unsigned int	i = 0;
++
++	next_timeout = now + 3600;
++	if (isns_config.ic_registration_period == 0)
++		return next_timeout;
++
++	while (i < list->iol_count) {
++		isns_object_t	*obj;
++		uint64_t	stamp;
++		uint32_t	period;
++
++		obj = list->iol_data[i];
++		if (!ISNS_IS_ENTITY(obj))
++			goto next;
++
++		if (!isns_object_get_uint32(obj,
++					ISNS_TAG_REGISTRATION_PERIOD,
++					&period)) {
++			isns_debug_state("No registration period for entity %u\n",
++					obj->ie_index);
++			goto next;
++		}
++
++		if (!isns_object_get_uint64(obj,
++					ISNS_TAG_TIMESTAMP,
++					&stamp)) {
++			isns_debug_state("No timestamp for entity %u\n",
++					obj->ie_index);
++			goto next;
++		}
++
++		stamp += period;
++		if (stamp <= now) {
++			/* removing the object will move one
++			 * object from the tail to the free
++			 * slot in the list. So don't increment
++			 * the index here. */
++			isns_debug_state("Expiring entity %u\n", obj->ie_index);
++			isns_db_remove(db, obj);
++			goto next;
++		} else {
++			isns_debug_state("Entity %u will expire in %u sec\n",
++					obj->ie_index, (int) (stamp - now));
++			if (stamp < next_timeout)
++				next_timeout = stamp;
++		}
++
++next:
++		i += 1;
++	}
++
++	/* Send out SCN notifications.
++	 * This makes sure we won't have extraneous references
++	 * on expired objects when we reach db_purge. */
++	isns_flush_events();
++
++	return next_timeout;
++}
++
++/*
++ * Very special function to make sure we always have a
++ * CONTROL entity.
++ */
++isns_object_t *
++isns_db_get_control(isns_db_t *db)
++{
++	isns_attr_list_t keys = ISNS_ATTR_LIST_INIT;
++	isns_object_list_t *list = db->id_objects;
++	isns_object_t	*found = NULL;
++	unsigned int	i;
++
++	isns_attr_list_append_string(&keys,
++			ISNS_TAG_ENTITY_IDENTIFIER,
++			ISNS_ENTITY_CONTROL);
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t	*obj;
++
++		obj = list->iol_data[i];
++		if (!ISNS_IS_ENTITY(obj))
++			continue;
++		if (isns_object_match(obj, &keys)) {
++			obj->ie_users++;
++			found = obj;
++			goto done;
++		}
++	}
++
++	found = isns_create_object(&isns_entity_template,
++			&keys, NULL);
++	found->ie_flags |= ISNS_OBJECT_PRIVATE;
++	isns_db_insert(db, found);
++	isns_db_sync(db);
++
++done:
++	return found;
++}
++
++void
++isns_db_get_domainless(isns_db_t *db,
++		isns_object_template_t *tmpl,
++		isns_object_list_t *result)
++{
++	isns_object_list_t *list = db->id_objects;
++	unsigned int	i;
++
++	if (!tmpl)
++		return;
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t	*obj = list->iol_data[i];
++
++		if (obj->ie_template == tmpl
++		 && isns_bitvector_is_empty(obj->ie_membership))
++			isns_object_list_append(result, obj);
++	}
++}
++
++/*
++ * Create a relationship and store it in the DB
++ */
++void
++isns_db_create_relation(isns_db_t *db,
++		isns_object_t *relating_object,
++		unsigned int relation_type,
++		isns_object_t *subordinate_object1,
++		isns_object_t *subordinate_object2)
++{
++	isns_relation_t *rel;
++
++	rel = isns_create_relation(relating_object,
++			relation_type,
++			subordinate_object1,
++			subordinate_object2);
++	if (rel) {
++		isns_relation_add(db->id_relations, rel);
++		isns_relation_release(rel);
++	}
++}
++
++/*
++ * Get all objects related to @left through a relation
++ * of type @type.
++ */
++void
++isns_db_get_relationship_objects(isns_db_t *db,
++		const isns_object_t *left,
++		unsigned int relation_type,
++		isns_object_list_t *result)
++{
++	isns_relation_get_edge_objects(db->id_relations,
++			left, relation_type,
++			result);
++}
++
++/*
++ * Get the object relating left and right.
++ * Usually called to find the portal group connecting
++ * a portal and a storage node, or a DD connecting
++ * two storage nodes.
++ */
++isns_object_t *
++isns_db_get_relationship_object(isns_db_t *db,
++		const isns_object_t *left,
++		const isns_object_t *right,
++		unsigned int relation_type)
++{
++	isns_relation_t *rel;
++
++	/* Find a relation of the given type, connecting
++	 * the two objects. */
++	rel = isns_relation_find_edge(db->id_relations,
++			left, right, relation_type);
++
++	if (rel == NULL)
++		return NULL;
++
++	return isns_object_get(rel->ir_object);
++}
++
++/*
++ * See if a relationship exists
++ */
++int
++isns_db_relation_exists(isns_db_t *db,
++		const isns_object_t *relating_object,
++		const isns_object_t *left,
++		const isns_object_t *right,
++		unsigned int relation_type)
++{
++	return isns_relation_exists(db->id_relations,
++			relating_object,
++			left, right, relation_type);
++}
++
++/*
++ * Debug helper
++ */
++void
++isns_db_print(isns_db_t *db, isns_print_fn_t *fn)
++{
++	const isns_object_list_t *list = db->id_objects;
++	unsigned int	i;
++
++	fn("Dumping database contents\n"
++	   "Backend:     %s\n"
++	   "Last EID:    %u\n"
++	   "Last Index:  %u\n"
++	   ,
++	   db->id_backend->idb_name,
++	   db->id_last_eid,
++	   db->id_last_index);
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t *obj = list->iol_data[i];
++
++		fn("--------------\n"
++		   "Object:      index=%u type=<%s> state=%s",
++		   obj->ie_index,
++		   obj->ie_template->iot_name,
++		   isns_object_state_string(obj->ie_state));
++		if (obj->ie_container)
++			fn(" parent=%u", obj->ie_container->ie_index);
++		if (obj->ie_flags & ISNS_OBJECT_DIRTY)
++			fn(" DIRTY");
++		if (obj->ie_flags & ISNS_OBJECT_PRIVATE)
++			fn(" PRIVATE");
++		fn("\n");
++		isns_attr_list_print(&obj->ie_attrs, fn);
++	}
++}
++
++/*
++ * Generate a "random" entity identifier. This is used when
++ * a DevAttrReg request does not specify an entity, and the
++ * client's policy doesn't specify one either.
++ */
++const char *
++isns_db_generate_eid(isns_db_t *db, char *buf, size_t size)
++{
++	snprintf(buf, size, "isns.entity.%04d", db->id_last_eid);
++	db->id_last_eid++;
++	return buf;
++}
++
++/*
++ * Highly primitive transaction handling.
++ * This is really just a hack for the iSNS server code,
++ * which wants to go along creating objects, and back out
++ * if something goes wrong.
++ */
++void
++isns_db_begin_transaction(isns_db_t *db)
++{
++	if (db->id_in_transaction) {
++		isns_error("isns_db_begin_transaction: running into pending transaction\n");
++		isns_db_rollback(db);
++	}
++	db->id_in_transaction = 1;
++}
++
++void
++isns_db_commit(isns_db_t *db)
++{
++	/* Nothing yet */
++	db->id_in_transaction = 0;
++}
++
++void
++isns_db_rollback(isns_db_t *db)
++{
++	/* Nothing yet */
++	db->id_in_transaction = 0;
++}
+diff --git a/utils/open-isns/db.h b/utils/open-isns/db.h
+new file mode 100644
+index 0000000..148d930
+--- /dev/null
++++ b/utils/open-isns/db.h
+@@ -0,0 +1,147 @@
++/*
++ * iSNS object database
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_DB_H
++#define ISNS_DB_H
++
++#include "attrs.h"
++
++typedef struct isns_db_backend	isns_db_backend_t;
++
++/*
++ * In-memory portion of object database.
++ * Stable storage is provided by different
++ * backends.
++ */
++struct isns_db {
++	isns_object_list_t *	id_objects;
++	isns_object_list_t	__id_objects;
++
++	isns_relation_soup_t *	id_relations;
++
++	uint32_t		id_last_eid;
++	uint32_t		id_last_index;
++
++	isns_scope_t *		id_global_scope;
++	isns_scope_t *		id_default_scope;
++
++	isns_db_backend_t *	id_backend;
++
++	unsigned int		id_in_transaction : 1;
++	struct isns_db_trans *	id_transact;
++
++	/* This is for objects in limbo. When a client
++	 * calls DevAttrDereg, the object will first be
++	 * placed on the id_deferred list.
++	 * When we're done processing the message, we
++	 * invoke isns_db_purge, which looks at these
++	 * objects.
++	 *  -	if the reference count is 1, the object
++	 *	is deleted.
++	 *  -	otherwise, we assume the object is referenced
++	 *	by a discovery domain. In this case, we prune
++	 *	the attribute list down to the key attr(s)
++	 *	plus the index attribute, and move it to
++	 *	the id_limbo list.
++	 */
++	isns_object_list_t	id_deferred;
++	isns_object_list_t	id_limbo;
++};
++
++
++struct isns_db_backend {
++	char *		idb_name;
++
++	int		(*idb_reload)(isns_db_t *);
++	int		(*idb_sync)(isns_db_t *);
++	int		(*idb_store)(isns_db_t *,
++					const isns_object_t *);
++	int		(*idb_remove)(isns_db_t *,
++					const isns_object_t *);
++};
++
++extern isns_db_backend_t *isns_create_file_db_backend(const char *);
++extern isns_object_t *	__isns_db_get_next(const isns_object_list_t *,
++					isns_object_template_t *,
++					const isns_attr_list_t *,
++					const isns_attr_list_t *);
++
++extern isns_relation_soup_t *isns_relation_soup_alloc(void);
++extern isns_relation_t *isns_create_relation(isns_object_t *relating_object,
++					unsigned int relation_type,
++					isns_object_t *subordinate_object1,
++					isns_object_t *subordinate_object2);
++extern void		isns_relation_sever(isns_relation_t *);
++extern void		isns_relation_release(isns_relation_t *);
++extern void		isns_relation_add(isns_relation_soup_t *,
++					isns_relation_t *);
++extern void		isns_relation_remove(isns_relation_soup_t *,
++					isns_relation_t *);
++extern isns_object_t *	isns_relation_get_other(const isns_relation_t *,
++					const isns_object_t *);
++extern isns_relation_t *isns_relation_find_edge(isns_relation_soup_t *,
++					const isns_object_t *,
++					const isns_object_t *,
++					unsigned int);
++extern void		isns_relation_halfspace(isns_relation_soup_t *,
++					const isns_object_t *,
++					unsigned int,
++					isns_object_list_t *);
++extern void		isns_relation_get_edge_objects(isns_relation_soup_t *,
++					const isns_object_t *,
++					unsigned int,
++					isns_object_list_t *);
++extern int		isns_relation_exists(isns_relation_soup_t *,
++					const isns_object_t *relating_object,
++					const isns_object_t *left,
++					const isns_object_t *right,
++					unsigned int relation_type);
++extern int		isns_relation_is_dead(const isns_relation_t *);
++
++extern void		isns_db_create_relation(isns_db_t *db,
++					isns_object_t *relating_object,
++					unsigned int relation_type,
++					isns_object_t *subordinate_object1,
++					isns_object_t *subordinate_object2);
++extern void		isns_db_get_relationship_objects(isns_db_t *,
++					const isns_object_t *,
++					unsigned int relation_type,
++					isns_object_list_t *);
++extern isns_object_t *	isns_db_get_relationship_object(isns_db_t *,
++					const isns_object_t *,
++					const isns_object_t *,
++					unsigned int relation_type);
++extern int		isns_db_relation_exists(isns_db_t *db,
++					const isns_object_t *relating_object,
++					const isns_object_t *left,
++					const isns_object_t *right,
++					unsigned int relation_type);
++extern int		isns_db_create_pg_relation(isns_db_t *,
++					isns_object_t *);
++
++extern isns_scope_t *	isns_scope_for_call(isns_db_t *, const isns_simple_t *);
++extern isns_scope_t *	isns_scope_alloc(isns_db_t *);
++extern void		isns_scope_release(isns_scope_t *);
++extern void		isns_scope_add(isns_scope_t *,
++				isns_object_t *);
++extern int		isns_scope_remove(isns_scope_t *,
++				isns_object_t *);
++extern int		isns_scope_gang_lookup(isns_scope_t *,
++				isns_object_template_t *,
++				const isns_attr_list_t *,
++				isns_object_list_t *);
++extern isns_object_t *	isns_scope_get_next(isns_scope_t *,
++				isns_object_template_t *,
++				const isns_attr_list_t *current,
++				const isns_attr_list_t *scope);
++extern void		isns_scope_get_related(isns_scope_t *,
++				const isns_object_t *,
++				unsigned int,
++				isns_object_list_t *);
++extern isns_db_t *	isns_scope_get_db(const isns_scope_t *);
++
++
++#endif /* ISNS_DB_H */
+diff --git a/utils/open-isns/dd.c b/utils/open-isns/dd.c
+new file mode 100644
+index 0000000..c2dcd10
+--- /dev/null
++++ b/utils/open-isns/dd.c
+@@ -0,0 +1,1306 @@
++/*
++ * Handle DD registration/deregistration
++ *
++ * Discovery domains are weird, even in the context of
++ * iSNS. For once thing, all other objects have unique
++ * attributes; DDs attributes can appear several times.
++ * They should really have made each DD member an object
++ * in its own right.
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include "isns.h"
++#include "attrs.h"
++#include "objects.h"
++#include "message.h"
++#include "security.h"
++#include "util.h"
++#include "db.h"
++
++#define DD_DEBUG
++
++enum {
++	ISNS_DD_MEMBER_ISCSI_NODE = 1,
++	ISNS_DD_MEMBER_IFCP_NODE,
++	ISNS_DD_MEMBER_PORTAL,
++};
++/* Must be zero/one: */
++enum {
++	NOTIFY_MEMBER_ADDED = 0,
++	NOTIFY_MEMBER_REMOVED = 1
++};
++
++typedef struct isns_dd isns_dd_t;
++typedef struct isns_dd_list isns_dd_list_t;
++typedef struct isns_dd_member isns_dd_member_t;
++
++struct isns_dd {
++	uint32_t		dd_id;
++	char *			dd_name;
++	uint32_t		dd_features;
++	isns_dd_member_t *	dd_members;
++
++	unsigned int		dd_inserted : 1;
++
++	isns_object_t *		dd_object;
++};
++
++struct isns_dd_member {
++	isns_dd_member_t *	ddm_next;
++	unsigned int		ddm_type;
++	isns_object_ref_t	ddm_object;
++
++	unsigned int		ddm_added : 1;
++	union {
++	    uint32_t		ddm_index;
++
++	    /* Index must be first in all structs below.
++	     * Yeah, I know. Aliasing is bad. */
++	    struct isns_dd_portal {
++		uint32_t	index;
++	        isns_portal_info_t info;
++	    } ddm_portal;
++	    struct isns_dd_iscsi_node {
++		uint32_t	index;
++		char *		name;
++	    } ddm_iscsi_node;
++	    struct isns_dd_ifcp_node {
++		uint32_t	index;
++		char *		name;
++	    } ddm_ifcp_node;
++	};
++};
++
++struct isns_dd_list {
++	unsigned int		ddl_count;
++	isns_dd_t **		ddl_data;
++};
++
++/*
++ * List of all discovery domains.
++ * This duplicates the DD information from the database,
++ * but unfortunately this can't be helped - we need to
++ * have fast algorithms to compute the membership of a
++ * node, and the relative visibility of two nodes.
++ */
++static int			isns_dd_list_initialized = 0;
++static isns_dd_list_t		isns_dd_list;
++static uint32_t			isns_dd_next_id = 1;
++
++static isns_dd_t *		isns_dd_alloc(void);
++static isns_dd_t *		isns_dd_clone(const isns_dd_t *);
++static void			isns_dd_release(isns_dd_t *);
++static int			isns_dd_parse_attrs(isns_dd_t *,
++					isns_db_t *, const isns_attr_list_t *,
++					const isns_dd_t *, int);
++static int			isns_dd_remove_members(isns_dd_t *,
++					isns_db_t *,
++					isns_dd_t *);
++static void			isns_dd_notify(const isns_dd_t *,
++					isns_dd_member_t *,
++					isns_dd_member_t *,
++					int);
++static void			isns_dd_add_members(isns_dd_t *,
++					isns_db_t *,
++					isns_dd_t *);
++static void			isns_dd_store(isns_db_t *, const isns_dd_t *, int);
++static void			isns_dd_destroy(isns_db_t *, isns_dd_t *);
++static void			isns_dd_insert(isns_dd_t *);
++static isns_dd_t *		isns_dd_by_id(uint32_t);
++static isns_dd_t *		isns_dd_by_name(const char *);
++static isns_dd_member_t *	isns_dd_create_member(isns_object_t *);
++static inline void		isns_dd_member_free(isns_dd_member_t *);
++static int			isns_dd_remove_member(isns_dd_t *, isns_object_t *);
++static void			isns_dd_list_resize(isns_dd_list_t *, unsigned int);
++static void			isns_dd_list_insert(isns_dd_list_t *, isns_dd_t *);
++static void			isns_dd_list_remove(isns_dd_list_t *, isns_dd_t *);
++
++static isns_object_t *		isns_dd_get_member_object(isns_db_t *,
++					const isns_attr_t *, const isns_attr_t *,
++					int);
++
++/*
++ * Create DDReg messages
++ */
++isns_simple_t *
++isns_create_dd_registration(isns_client_t *clnt, const isns_attr_list_t *attrs)
++{
++	isns_simple_t	*msg;
++	isns_attr_t	*id_attr;
++
++	msg = isns_simple_create(ISNS_DD_REGISTER, clnt->ic_source, NULL);
++	if (msg == NULL)
++		return NULL;
++
++	/* If the caller specified a DD_ID, use it in the
++	 * message key. */
++	if (isns_attr_list_get_attr(attrs, ISNS_TAG_DD_ID, &id_attr))
++		isns_attr_list_append_attr(&msg->is_message_attrs, id_attr);
++
++	isns_attr_list_copy(&msg->is_operating_attrs, attrs);
++	return msg;
++}
++
++isns_simple_t *
++isns_create_dd_deregistration(isns_client_t *clnt,
++		uint32_t dd_id, const isns_attr_list_t *attrs)
++{
++	isns_simple_t	*msg;
++
++	msg = isns_simple_create(ISNS_DD_DEREGISTER, clnt->ic_source, NULL);
++	if (msg == NULL)
++		return NULL;
++
++	isns_attr_list_append_uint32(&msg->is_message_attrs,
++			ISNS_TAG_DD_ID, dd_id);
++
++	isns_attr_list_copy(&msg->is_operating_attrs, attrs);
++	return msg;
++}
++
++/*
++ * Process a DD registration
++ */
++int
++isns_process_dd_registration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
++{
++	isns_simple_t	*reply = NULL;
++	isns_attr_list_t *keys = &call->is_message_attrs;
++	isns_attr_list_t *attrs = &call->is_operating_attrs;
++	isns_db_t	*db = srv->is_db;
++	isns_dd_t	*dd = NULL, *temp_dd = NULL;
++	isns_attr_t	*attr;
++	uint32_t	id = 0;
++	int		status;
++
++	/*
++	 * 5.6.5.9.
++	 * The Message Key, if used, contains the DD_ID of the Discovery
++	 * Domain to be registered.  If the Message Key contains a DD_ID
++	 * of an existing DD entry in the iSNS database, then the DDReg
++	 * message SHALL attempt to update the existing entry.	If the
++	 * DD_ID in the Message Key (if used) does not match an existing
++	 * DD entry, then the iSNS server SHALL reject the DDReg message
++	 * with a status code of 3 (Invalid Registration).
++	 */
++	switch (keys->ial_count) {
++	case 0:
++		/* Security: check if the client is allowed to
++		 * create a discovery domain */
++		if (!isns_policy_validate_object_creation(call->is_policy,
++					call->is_source,
++					&isns_dd_template,
++					keys, attrs,
++					call->is_function))
++			goto unauthorized;
++		break;
++
++	case 1:
++		attr = keys->ial_data[0];
++		if (attr->ia_tag_id != ISNS_TAG_DD_ID)
++			goto reject;
++		if (ISNS_ATTR_IS_NIL(attr))
++			break;
++		if (!ISNS_ATTR_IS_UINT32(attr))
++			goto reject;
++
++		id = attr->ia_value.iv_uint32;
++		if (id == 0)
++			goto reject;
++
++		dd = isns_dd_by_id(id);
++		if (dd == NULL) {
++			isns_debug_state("DDReg for unknown ID=%u\n", id);
++			goto reject;
++		}
++
++		/* Security: check if the client is allowed to
++		 * mess with this DD. */
++		isns_assert(dd->dd_object);
++		if (!isns_policy_validate_object_update(call->is_policy,
++					call->is_source,
++					dd->dd_object, attrs,
++					call->is_function))
++			goto unauthorized;
++
++		break;
++
++	default:
++		goto reject;
++	}
++
++	temp_dd = isns_dd_alloc();
++
++	/* Parse the attributes and build a DD object. */
++	status = isns_dd_parse_attrs(temp_dd, db, attrs, dd, 1);
++	if (status != ISNS_SUCCESS)
++		goto out;
++
++	if (dd == NULL) {
++		/* Create the DD, and copy the general information
++		 * such asn features and symbolic name from temp_dd */
++		dd = isns_dd_clone(temp_dd);
++
++		/* Don't assign the attrs to the DD right away.
++		 * First and foremost, they may be unsorted. Second,
++		 * we really want to hand-pick through them due to
++		 * the weird semantics mandated by the RFC. */
++		dd->dd_object = isns_create_object(&isns_dd_template, NULL, NULL);
++		if (dd->dd_object == NULL)
++			goto reject;
++
++		/* Insert new domain into database */
++		isns_db_insert(db, dd->dd_object);
++
++		/* Add it to the internal list. Assign DD_ID and
++		 * symbolic name if none were given.
++		 */
++		isns_dd_insert(dd);
++	} else {
++		if (!dd->dd_id)
++			dd->dd_id = temp_dd->dd_id;
++		dd->dd_features = temp_dd->dd_features;
++		isns_assign_string(&dd->dd_name, temp_dd->dd_name);
++	}
++
++	/* Send notifications. This must be done before merging
++	 * the list of new members into the DD. 
++	 */
++	isns_dd_notify(dd, dd->dd_members, temp_dd->dd_members,
++			NOTIFY_MEMBER_ADDED);
++
++	/* Update the DD */
++	isns_dd_add_members(dd, db, temp_dd);
++
++	/* And add it to the database. */
++	isns_dd_store(db, dd, 0);
++
++	reply = isns_simple_create(ISNS_DD_REGISTER, srv->is_source, NULL);
++	isns_object_extract_all(dd->dd_object, &reply->is_operating_attrs);
++
++	status = ISNS_SUCCESS;
++
++out:
++	isns_dd_release(temp_dd);
++	isns_dd_release(dd);
++	*result = reply;
++	return status;
++
++reject:
++	status = ISNS_INVALID_REGISTRATION;
++	goto out;
++
++unauthorized:
++	status = ISNS_SOURCE_UNAUTHORIZED;
++	goto out;
++}
++
++/*
++ * Process a DD deregistration
++ */
++int
++isns_process_dd_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
++{
++	isns_simple_t	*reply = NULL;
++	isns_attr_list_t *keys = &call->is_message_attrs;
++	isns_attr_list_t *attrs = &call->is_operating_attrs;
++	isns_db_t	*db = srv->is_db;
++	isns_dd_t	*dd = NULL, *temp_dd = NULL;
++	isns_attr_t	*attr;
++	uint32_t	id = 0;
++	int		status;
++
++	/*
++	 * 5.6.5.10.
++	 * The Message Key Attribute for a DDDereg message is the DD
++	 * ID for the Discovery Domain being removed or having members
++	 * removed.
++	 */
++	if (keys->ial_count != 1)
++		goto reject;
++
++	attr = keys->ial_data[0];
++	if (attr->ia_tag_id != ISNS_TAG_DD_ID
++	 || ISNS_ATTR_IS_NIL(attr)
++	 || !ISNS_ATTR_IS_UINT32(attr))
++		goto reject;
++
++	id = attr->ia_value.iv_uint32;
++	if (id == 0)
++		goto reject;
++
++	dd = isns_dd_by_id(id);
++	if (dd == NULL)
++		goto reject;
++
++	/* Security: check if the client is permitted to
++	 * modify the DD object.
++	 */
++	if (!isns_policy_validate_object_update(call->is_policy,
++				call->is_source,
++				dd->dd_object, attrs,
++				call->is_function))
++		goto unauthorized;
++
++	/* 
++	 * 5.6.5.10.
++	 * If the DD ID matches an existing DD and there are
++	 * no Operating Attributes, then the DD SHALL be removed and a
++	 * success Status Code returned.  Any existing members of that
++	 * DD SHALL remain in the iSNS database without membership in
++	 * the just-removed DD.
++	 */
++	if (attrs->ial_count == 0) {
++		isns_dd_member_t	*mp;
++
++		/* Zap the membership bit */
++		for (mp = dd->dd_members; mp; mp = mp->ddm_next) {
++			isns_object_t	*obj = mp->ddm_object.obj;
++
++			isns_object_clear_membership(obj, dd->dd_id);
++		}
++
++		/* Notify all DD members that they will lose the other
++		 * nodes. */
++		isns_dd_notify(dd, NULL, dd->dd_members, NOTIFY_MEMBER_REMOVED);
++
++		isns_dd_destroy(db, dd);
++	} else {
++		/* Parse the attributes and build a temporary DD object. */
++		temp_dd = isns_dd_alloc();
++		status = isns_dd_parse_attrs(temp_dd, db, attrs, dd, 0);
++		if (status != ISNS_SUCCESS)
++			goto out;
++
++		/* Update the DD object */
++		status = isns_dd_remove_members(dd, db, temp_dd);
++		if (status != ISNS_SUCCESS)
++			goto out;
++
++		/* Send notifications. This must be done before after
++		 * updating the DD.
++		 */
++		isns_dd_notify(dd, dd->dd_members, temp_dd->dd_members,
++				NOTIFY_MEMBER_REMOVED);
++
++		/* Store it in the database. */
++		isns_dd_store(db, dd, 1);
++	}
++
++	reply = isns_simple_create(ISNS_DD_DEREGISTER, srv->is_source, NULL);
++	status = ISNS_SUCCESS;
++
++out:
++	isns_dd_release(temp_dd);
++	isns_dd_release(dd);
++	*result = reply;
++	return status;
++
++reject:
++	status = ISNS_INVALID_DEREGISTRATION;
++	goto out;
++
++unauthorized:
++	status = ISNS_SOURCE_UNAUTHORIZED;
++	goto out;
++}
++
++static isns_dd_t *
++isns_dd_alloc(void)
++{
++	return isns_calloc(1, sizeof(isns_dd_t));
++}
++
++/*
++ * Allocate a clone of the orig_dd, but without
++ * copying the members.
++ */
++static isns_dd_t *
++isns_dd_clone(const isns_dd_t *orig_dd)
++{
++	isns_dd_t	*dd;
++
++	dd = isns_dd_alloc();
++
++	dd->dd_id = orig_dd->dd_id;
++	dd->dd_features = orig_dd->dd_features;
++	dd->dd_object = isns_object_get(orig_dd->dd_object);
++	isns_assign_string(&dd->dd_name, orig_dd->dd_name);
++
++	return dd;
++}
++
++static void
++isns_dd_release(isns_dd_t *dd)
++{
++	isns_dd_member_t *member;
++
++	if (dd == NULL || dd->dd_inserted)
++		return;
++
++	while ((member = dd->dd_members) != NULL) {
++		dd->dd_members = member->ddm_next;
++		isns_dd_member_free(member);
++	}
++
++	if (dd->dd_object)
++		isns_object_release(dd->dd_object);
++
++	isns_free(dd->dd_name);
++	isns_free(dd);
++}
++
++static isns_dd_member_t *
++isns_dd_create_member(isns_object_t *obj)
++{
++	isns_dd_member_t *new;
++
++	new = isns_calloc(1, sizeof(*new));
++	new->ddm_added = 1;
++
++	if (ISNS_IS_ISCSI_NODE(obj))
++		new->ddm_type = ISNS_DD_MEMBER_ISCSI_NODE;
++	else if (ISNS_IS_PORTAL(obj))
++		new->ddm_type = ISNS_DD_MEMBER_PORTAL;
++	else if (ISNS_IS_FC_NODE(obj))
++		new->ddm_type = ISNS_DD_MEMBER_IFCP_NODE;
++	else {
++		isns_free(new);
++		return NULL;
++	}
++
++	isns_object_reference_set(&new->ddm_object, obj);
++	return new;
++}
++
++static inline void
++isns_dd_member_free(isns_dd_member_t *member)
++{
++	switch (member->ddm_type) {
++	case ISNS_DD_MEMBER_ISCSI_NODE:
++		isns_free(member->ddm_iscsi_node.name);
++		break;
++
++	case ISNS_DD_MEMBER_IFCP_NODE:
++		isns_free(member->ddm_ifcp_node.name);
++		break;
++	}
++
++	isns_object_reference_drop(&member->ddm_object);
++	isns_free(member);
++}
++
++void
++isns_dd_get_members(uint32_t dd_id, isns_object_list_t *list, int active_only)
++{
++	isns_dd_t	*dd;
++	isns_dd_member_t *mp;
++
++	dd = isns_dd_by_id(dd_id);
++	if (dd == NULL)
++		return;
++
++	for (mp = dd->dd_members; mp; mp = mp->ddm_next) {
++		isns_object_t	*obj = mp->ddm_object.obj;
++
++		if (active_only
++		 && obj->ie_state != ISNS_OBJECT_STATE_MATURE)
++			continue;
++
++		isns_object_list_append(list, obj);
++	}
++}
++
++/*
++ * Helper function to remove a member referencing the given object
++ */
++static int
++isns_dd_remove_member(isns_dd_t *dd, isns_object_t *obj)
++{
++	isns_dd_member_t *mp, **pos;
++
++	pos = &dd->dd_members;
++	while ((mp = *pos) != NULL) {
++		if (mp->ddm_object.obj == obj) {
++			*pos = mp->ddm_next;
++			isns_dd_member_free(mp);
++			return 1;
++		} else {
++			pos = &mp->ddm_next;
++		}
++	}
++
++	return 0;
++}
++
++static void
++isns_dd_insert(isns_dd_t *dd)
++{
++	if (dd->dd_inserted)
++		return;
++
++	if (dd->dd_id == 0) {
++		uint32_t	id = isns_dd_next_id;
++		unsigned int	i;
++
++		for (i = 0; i < isns_dd_list.ddl_count; ++i) {
++			isns_dd_t *cur = isns_dd_list.ddl_data[i];
++
++			if (cur->dd_id > id)
++				break;
++			if (cur->dd_id == id)
++				++id;
++		}
++		isns_debug_state("Allocated new DD_ID %d\n", id);
++		dd->dd_id = id;
++		isns_dd_next_id = id + 1;
++	}
++
++	/*
++	 * When creating a new DD, if the DD_Symbolic_Name is
++	 * not included in the Operating Attributes, or if it
++	 * is included with a zero-length TLV, then the iSNS
++	 * server SHALL provide a unique DD_Symbolic_Name value
++	 * for the created DD.	The assigned DD_Symbolic_Name
++	 * value SHALL be returned in the DDRegRsp message.
++	 */
++	if (dd->dd_name == NULL) {
++		char	namebuf[64];
++
++		snprintf(namebuf, sizeof(namebuf), "isns.dd%u", dd->dd_id);
++		isns_assign_string(&dd->dd_name, namebuf);
++	}
++
++	isns_dd_list_insert(&isns_dd_list, dd);
++	dd->dd_inserted = 1;
++
++#ifdef DD_DEBUG
++	/* Safety first - make sure domains are sorted by DD_ID */
++	{
++		unsigned int i, prev_id = 0;
++
++		for (i = 0; i < isns_dd_list.ddl_count; ++i) {
++			isns_dd_t *cur = isns_dd_list.ddl_data[i];
++
++			isns_assert(cur->dd_id > prev_id);
++			prev_id = cur->dd_id;
++		}
++	}
++#endif
++}
++
++/*
++ * Resize the DD list
++ */
++#define LIST_SIZE(n)	(((n) + 15) & ~15)
++void
++isns_dd_list_resize(isns_dd_list_t *list, unsigned int last_index)
++{
++	unsigned int	new_size;
++	isns_dd_t	**new_data;
++
++	new_size = LIST_SIZE(last_index + 1);
++	if (new_size < list->ddl_count)
++		return;
++
++	/* We don't use realloc here because we need
++	 * to zero the new pointers anyway. */
++	new_data = isns_calloc(new_size, sizeof(void *));
++	isns_assert(new_data);
++
++	memcpy(new_data, list->ddl_data,
++			list->ddl_count * sizeof(void *));
++	isns_free(list->ddl_data);
++
++	list->ddl_data =  new_data;
++	list->ddl_count = last_index + 1;
++}
++
++/*
++ * Find the insert position for a given DD ID.
++ * returns true iff the DD was found in the list.
++ */
++static int
++__isns_dd_list_find_pos(isns_dd_list_t *list, unsigned int id,
++			unsigned int *where)
++{
++	unsigned int	hi, lo, md;
++
++	lo = 0;
++	hi = list->ddl_count;
++
++	/* binary search */
++	while (lo < hi) {
++		isns_dd_t *cur;
++
++		md = (lo + hi) / 2;
++		cur = list->ddl_data[md];
++
++		if (id == cur->dd_id) {
++			*where = md;
++			return 1;
++		}
++
++		if (id < cur->dd_id) {
++			hi = md;
++		} else {
++			lo = md + 1;
++		}
++	}
++
++	*where = hi;
++	return 0;
++}
++
++/*
++ * In-order insert
++ */
++static void
++isns_dd_list_insert(isns_dd_list_t *list, isns_dd_t *dd)
++{
++	unsigned int	pos;
++
++	if (__isns_dd_list_find_pos(list, dd->dd_id, &pos)) {
++		isns_error("Internal error in %s: DD already listed\n",
++				__FUNCTION__);
++		return;
++	}
++
++	isns_dd_list_resize(list, list->ddl_count);
++	/* Shift the tail of the list to make room for new entry. */
++	memmove(list->ddl_data + pos + 1,
++		list->ddl_data + pos,
++		(list->ddl_count - pos - 1) * sizeof(void *));
++	list->ddl_data[pos] = dd;
++}
++
++/*
++ * Remove DD from list
++ */
++void
++isns_dd_list_remove(isns_dd_list_t *list, isns_dd_t *dd)
++{
++	unsigned int	pos;
++
++	if (!__isns_dd_list_find_pos(list, dd->dd_id, &pos))
++		return;
++
++	/* Shift the tail of the list */
++	memmove(list->ddl_data + pos,
++		list->ddl_data + pos + 1,
++		(list->ddl_count - pos - 1) * sizeof(void *));
++	list->ddl_count -= 1;
++}
++
++isns_dd_t *
++isns_dd_by_id(uint32_t id)
++{
++	unsigned int	i;
++	
++	for (i = 0; i < isns_dd_list.ddl_count; ++i) {
++		isns_dd_t *dd = isns_dd_list.ddl_data[i];
++
++		if (dd && dd->dd_id == id)
++			return dd;
++	}
++
++	return NULL;
++}
++
++static isns_dd_t *
++isns_dd_by_name(const char *name)
++{
++	unsigned int	i;
++	
++	for (i = 0; i < isns_dd_list.ddl_count; ++i) {
++		isns_dd_t *dd = isns_dd_list.ddl_data[i];
++
++		if (dd && !strcmp(dd->dd_name, name))
++			return dd;
++	}
++
++	return NULL;
++}
++
++/*
++ * Validate the operating attributes, which is surprisingly
++ * tedious for DDs. It appears as if the whole DD/DDset
++ * stuff has been slapped onto iSNS as an afterthought.
++ *
++ * DDReg has some funky rules about how eg iSCSI nodes
++ * can be identified by either name or index, and how they
++ * relate to each other. Unfortunately, the RFC is very vague
++ * in describing how to treat DDReg message that mix these
++ * two types of identification, except by saying they
++ * need to be consistent.
++ */
++static int
++isns_dd_parse_attrs(isns_dd_t *dd, isns_db_t *db,
++		const isns_attr_list_t *attrs,
++		const isns_dd_t *orig_dd,
++		int is_registration)
++{
++	isns_dd_member_t **tail;
++	const isns_dd_t	*conflict;
++	unsigned int	i;
++	int		rv = ISNS_SUCCESS;
++
++	if (orig_dd) {
++		dd->dd_id = orig_dd->dd_id;
++		dd->dd_features = orig_dd->dd_features;
++		isns_assign_string(&dd->dd_name, orig_dd->dd_name);
++	}
++
++	isns_assert(dd->dd_members == NULL);
++	tail = &dd->dd_members;
++
++	for (i = 0; i < attrs->ial_count; ++i) {
++		isns_object_t	*obj = NULL;
++		isns_attr_t	*attr, *next = NULL;
++		const char	*name;
++		uint32_t	id;
++
++		attr = attrs->ial_data[i];
++
++		if (!isns_object_attr_valid(&isns_dd_template, attr->ia_tag_id))
++			return ISNS_INVALID_REGISTRATION;
++
++		switch (attr->ia_tag_id) {
++		case ISNS_TAG_DD_ID:
++			/* Ignore this attribute in DDDereg messages */
++			if (!is_registration)
++				continue;
++
++			/*
++			 * 5.6.5.9.
++			 * A DDReg message with no Message Key SHALL result
++			 * in the attempted creation of a new Discovery Domain
++			 * (DD).  If the DD_ID attribute (with non-zero length)
++			 * is included among the Operating Attributes in the
++			 * DDReg message, then the new Discovery Domain SHALL be
++			 * assigned the value contained in that DD_ID attribute.
++			 *
++			 * If the DD_ID is included in both the Message
++			 * Key and Operating Attributes, then the DD_ID
++			 * value in the Message Key MUST be the same as
++			 * the DD_ID value in the Operating Attributes.
++			 *
++			 * Implementer's note: It's not clear why the standard
++			 * makes an exception for the DD_ID, while all other
++			 * index attributes are read-only.
++			 */
++			if (ISNS_ATTR_IS_NIL(attr))
++				break;
++
++			id = attr->ia_value.iv_uint32;
++			if (dd->dd_id != 0) {
++				if (dd->dd_id != id)
++					goto invalid;
++			} else if ((conflict = isns_dd_by_id(id)) != NULL) {
++				isns_debug_state("DDReg: requested ID %d "
++						"clashes with existing DD (%s)\n",
++						id, conflict->dd_name);
++				goto invalid;
++			}
++			dd->dd_id = id;
++			break;
++
++		case ISNS_TAG_DD_SYMBOLIC_NAME:
++			/* Ignore this attribute in DDDereg messages */
++			if (!is_registration)
++				continue;
++
++			/*
++			 * If the DD_Symbolic_Name is an operating
++			 * attribute and its value is unique (i.e., it
++			 * does not match the registered DD_Symbolic_Name
++			 * for another DD), then the value SHALL be stored
++			 * in the iSNS database as the DD_Symbolic_Name
++			 * for the specified Discovery Domain.	If the
++			 * value for the DD_Symbolic_Name is not unique,
++			 * then the iSNS server SHALL reject the attempted
++			 * DD registration with a status code of 3
++			 * (Invalid Registration).
++			 */
++			if (ISNS_ATTR_IS_NIL(attr))
++				break;
++
++			name = attr->ia_value.iv_string;
++			if (dd->dd_name && strcmp(name, dd->dd_name)) {
++				isns_debug_state("DDReg: symbolic name conflict: "
++						"id=%d name=%s requested=%s\n",
++						dd->dd_id, dd->dd_name, name);
++				goto invalid;
++			}
++			if (dd->dd_name)
++				break;
++
++			if ((conflict = isns_dd_by_name(name)) != NULL) {
++				isns_debug_state("DDReg: requested symbolic name (%s) "
++						"clashes with existing DD (id=%d)\n",
++						name, conflict->dd_id);
++				goto invalid;
++			}
++			isns_assign_string(&dd->dd_name, name);
++			break;
++
++		case ISNS_TAG_DD_FEATURES:
++			/* Ignore this attribute in DDDereg messages */
++			if (!is_registration)
++				continue;
++
++			/*
++			 * When creating a new DD, if the DD_Features
++			 * attribute is not included in the Operating
++			 * Attributes, then the iSNS server SHALL assign
++			 * the default value.  The default value for
++			 * DD_Features is 0.
++			 */
++			if (ISNS_ATTR_IS_UINT32(attr))
++				dd->dd_features = attr->ia_value.iv_uint32;
++			break;
++
++		case ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR:
++			/* portal address must be followed by port */
++			if (i + 1 >= attrs->ial_count)
++				goto invalid;
++
++			next = attrs->ial_data[i + 1];
++			if (next->ia_tag_id != ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT)
++				goto invalid;
++			i += 1;
++			/* fallthru to normal case */
++
++		case ISNS_TAG_DD_MEMBER_PORTAL_INDEX:
++		case ISNS_TAG_DD_MEMBER_ISCSI_INDEX:
++		case ISNS_TAG_DD_MEMBER_ISCSI_NAME:
++		case ISNS_TAG_DD_MEMBER_FC_PORT_NAME:
++			if (ISNS_ATTR_IS_NIL(attr))
++				goto invalid;
++
++			obj = isns_dd_get_member_object(db,
++					attr, next,
++					is_registration);
++			/* For a DD deregistration, it's okay if the
++			 * object does not exist. */
++			if (obj == NULL && is_registration)
++				goto invalid;
++			break;
++
++		invalid:
++			rv = ISNS_INVALID_REGISTRATION;
++			continue;
++
++		}
++
++		if (obj) {
++			if (is_registration
++			 && isns_object_test_membership(obj, dd->dd_id)) {
++				/* Duplicates are ignored */
++				isns_debug_state("Ignoring duplicate DD registration "
++						 "for %s %u\n",
++						 obj->ie_template->iot_name,
++						 obj->ie_index);
++			} else {
++				/* This just adds the member to the temporary DD object,
++				 * without changing any state in the database. */
++				isns_dd_member_t *new;
++
++				new = isns_dd_create_member(obj);
++				if (new) {
++					*tail = new;
++					tail = &new->ddm_next;
++				}
++			}
++			isns_object_release(obj);
++		}
++	}
++
++	return rv;
++}
++
++/*
++ * Helper function: extract live nodes from the DD member list
++ */
++static inline void
++isns_dd_get_member_nodes(isns_dd_member_t *members, isns_object_list_t *result)
++{
++	isns_dd_member_t	*mp;
++
++	/* Extract iSCSI nodes from both list. */
++	for (mp = members; mp; mp = mp->ddm_next) {
++		isns_object_t	*obj = mp->ddm_object.obj;
++
++		if (ISNS_IS_ISCSI_NODE(obj)
++		 && obj->ie_state == ISNS_OBJECT_STATE_MATURE)
++			isns_object_list_append(result, obj);
++	}
++}
++
++void
++isns_dd_notify(const isns_dd_t *dd, isns_dd_member_t *unchanged,
++		isns_dd_member_t *changed, int removed)
++{
++	isns_object_list_t	dd_objects = ISNS_OBJECT_LIST_INIT;
++	isns_object_list_t	changed_objects = ISNS_OBJECT_LIST_INIT;
++	unsigned int		i, j, event;
++
++	/* Extract iSCSI nodes from both list. */
++	isns_dd_get_member_nodes(unchanged, &dd_objects);
++	isns_dd_get_member_nodes(changed, &changed_objects);
++
++	/* Send a management SCN multicast to all
++	 * control nodes that care. */
++	event = removed? ISNS_SCN_DD_MEMBER_REMOVED_MASK : ISNS_SCN_DD_MEMBER_ADDED_MASK;
++	for (i = 0; i < changed_objects.iol_count; ++i) {
++		isns_object_t	*obj = changed_objects.iol_data[i];
++
++		isns_object_event(obj,
++				event | ISNS_SCN_MANAGEMENT_REGISTRATION_MASK,
++				dd->dd_object);
++	}
++
++#ifdef notagoodidea
++	/* Not sure - it may be good to send OBJECT ADDED/REMOVED instead
++	 * of the DD membership messages. However, right now the SCN code
++	 * will nuke all SCN registrations for a node when it sees a
++	 * REMOVE event for it.
++	 */
++	event = removed? ISNS_SCN_OBJECT_REMOVED_MASK : ISNS_SCN_OBJECT_ADDED_MASK;
++#endif
++
++	/* If we added an iscsi node, loop over all members
++	 * and send unicast events to each iscsi node,
++	 * informing them that a new member has been added/removed.
++	 */
++	for (j = 0; j < changed_objects.iol_count; ++j) {
++		isns_object_t	*changed = changed_objects.iol_data[j];
++
++		for (i = 0; i < dd_objects.iol_count; ++i) {
++			isns_object_t	*obj = dd_objects.iol_data[i];
++
++			/* For member removal, do not send notifications
++			 * if the two nodes are still visible to each
++			 * other through a different discovery domain */
++			if (removed && isns_object_test_visibility(obj, changed))
++				continue;
++
++			/* Inform the old node that the new node was
++			 * added/removed. */
++			isns_unicast_event(obj, changed, event, NULL);
++
++			/* Inform the new node that the old node became
++			 * (in)accessible to it. */
++			isns_unicast_event(changed, obj, event, NULL);
++		}
++
++		/* Finally, inform each changed node of the other
++		 * DD members that became (in)accessible to it. */
++		for (i = 0; i < changed_objects.iol_count; ++i) {
++			isns_object_t	*obj = changed_objects.iol_data[i];
++
++			if (obj == changed)
++				continue;
++
++			if (removed && isns_object_test_visibility(obj, changed))
++				continue;
++
++			isns_unicast_event(changed, obj, event, NULL);
++		}
++	}
++}
++
++void
++isns_dd_add_members(isns_dd_t *dd, isns_db_t *db, isns_dd_t *new_dd)
++{
++	isns_dd_member_t *mp, **tail;
++
++	for (mp = new_dd->dd_members; mp; mp = mp->ddm_next) {
++		const char	*node_name;
++		isns_object_t	*obj = mp->ddm_object.obj;
++
++		/*
++		 * If the Operating Attributes contain a DD
++		 * Member iSCSI Name value for a Storage Node
++		 * that is currently not registered in the iSNS
++		 * database, then the iSNS server MUST allocate an
++		 * unused iSCSI Node Index for that Storage Node.
++		 * The assigned iSCSI Node Index SHALL be returned
++		 * in the DDRegRsp message as the DD Member iSCSI
++		 * Node Index.	The allocated iSCSI Node Index
++		 * value SHALL be assigned to the Storage Node
++		 * if and when it registers in the iSNS database.
++		 * [And likewise for portals]
++		 */
++		if (obj->ie_index == 0)
++			isns_db_insert_limbo(db, obj);
++		mp->ddm_index = obj->ie_index;
++
++		/* Record the fact that the object is a member of
++		 * this DD */
++		isns_object_mark_membership(obj, dd->dd_id);
++
++		switch (mp->ddm_type) {
++		case ISNS_DD_MEMBER_ISCSI_NODE:
++			if (isns_object_get_string(obj, ISNS_TAG_ISCSI_NAME, &node_name))
++				isns_assign_string(&mp->ddm_iscsi_node.name, node_name);
++
++			break;
++
++		case ISNS_DD_MEMBER_IFCP_NODE:
++			if (isns_object_get_string(obj, ISNS_TAG_FC_PORT_NAME_WWPN, &node_name))
++				isns_assign_string(&mp->ddm_ifcp_node.name, node_name);
++
++			break;
++
++		case ISNS_DD_MEMBER_PORTAL:
++			isns_portal_from_object(&mp->ddm_portal.info,
++					ISNS_TAG_PORTAL_IP_ADDRESS,
++					ISNS_TAG_PORTAL_TCP_UDP_PORT,
++					obj);
++			break;
++		}
++	}
++
++	/* Find the tail of the DD member list */
++	tail = &dd->dd_members;
++	while ((mp = *tail) != NULL)
++		tail = &mp->ddm_next;
++
++	/* Append the new list of members */
++	*tail = new_dd->dd_members;
++	new_dd->dd_members = NULL;
++}
++
++/*
++ * Remove members from a DD
++ */
++int
++isns_dd_remove_members(isns_dd_t *dd, isns_db_t *db, isns_dd_t *temp_dd)
++{
++	isns_dd_member_t *mp;
++
++	for (mp = temp_dd->dd_members; mp; mp = mp->ddm_next) {
++		isns_object_t	*obj = mp->ddm_object.obj;
++
++		/* Clear the membership bit. If the object wasn't in this
++		 * DD to begin with, bail out right away. */
++		if (!isns_object_clear_membership(obj, dd->dd_id)) {
++			isns_debug_state("DD dereg: object %d is not in this DD\n",
++						obj->ie_index);
++			continue;
++		}
++
++		if (!isns_dd_remove_member(dd, obj))
++			isns_error("%s: DD member not found in internal list\n",
++				__FUNCTION__);
++	}
++
++	return ISNS_SUCCESS;
++}
++
++void
++isns_dd_store(isns_db_t *db, const isns_dd_t *dd, int rewrite)
++{
++	isns_object_t	*obj = dd->dd_object;
++	isns_dd_member_t *member;
++
++	if (rewrite)
++		isns_object_prune_attrs(obj);
++
++	isns_object_set_uint32(obj, ISNS_TAG_DD_ID, dd->dd_id);
++	isns_object_set_string(obj, ISNS_TAG_DD_SYMBOLIC_NAME, dd->dd_name);
++	isns_object_set_uint32(obj, ISNS_TAG_DD_FEATURES, dd->dd_features);
++
++	for (member = dd->dd_members; member; member = member->ddm_next) {
++		struct isns_dd_iscsi_node *node;
++		struct isns_dd_portal *portal;
++
++		if (!member->ddm_added && !rewrite)
++			continue;
++
++		switch (member->ddm_type) {
++		case ISNS_DD_MEMBER_ISCSI_NODE:
++			node = &member->ddm_iscsi_node;
++
++			isns_object_set_uint32(obj,
++					ISNS_TAG_DD_MEMBER_ISCSI_INDEX,
++					node->index);
++			if (node->name)
++				isns_object_set_string(obj,
++					ISNS_TAG_DD_MEMBER_ISCSI_NAME,
++					node->name);
++			break;
++
++		case ISNS_DD_MEMBER_PORTAL:
++			portal = &member->ddm_portal;
++
++			isns_object_set_uint32(obj,
++					ISNS_TAG_DD_MEMBER_PORTAL_INDEX,
++					portal->index);
++			if (portal->info.addr.sin6_family != AF_UNSPEC) {
++				isns_portal_to_object(&portal->info,
++					ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR,
++					ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT,
++					obj);
++			}
++			break;
++		}
++
++		member->ddm_added = 0;
++	}
++}
++
++/*
++ * Destroy a DD
++ * The caller should call isns_dd_release to free the DD object.
++ */
++void
++isns_dd_destroy(isns_db_t *db, isns_dd_t *dd)
++{
++	isns_db_remove(db, dd->dd_object);
++	isns_dd_list_remove(&isns_dd_list, dd);
++	dd->dd_inserted = 0;
++}
++
++int
++isns_dd_load_all(isns_db_t *db)
++{
++	isns_object_list_t list = ISNS_OBJECT_LIST_INIT;
++	unsigned int	i;
++	int		rc;
++
++	if (isns_dd_list_initialized)
++		return ISNS_SUCCESS;
++
++	rc = isns_db_gang_lookup(db, &isns_dd_template, NULL, &list);
++	if (rc != ISNS_SUCCESS)
++		return rc;
++
++	for (i = 0; i < list.iol_count; ++i) {
++		isns_object_t *obj = list.iol_data[i];
++		isns_dd_t *dd = NULL, *temp_dd = NULL;
++		isns_dd_member_t *mp;
++
++		temp_dd = isns_dd_alloc();
++
++		rc = isns_dd_parse_attrs(temp_dd, db, &obj->ie_attrs, NULL, 1);
++		if (rc) {
++			if (temp_dd->dd_id == 0) {
++				isns_error("Problem converting DD object (index 0x%x). No DD_ID\n",
++					   obj->ie_index);
++				goto next;
++			}
++			isns_error("Problem converting DD %u. Proceeding anyway.\n",
++				   temp_dd->dd_id);
++		} else {
++			isns_debug_state("Loaded DD %d from database\n", temp_dd->dd_id);
++		}
++
++		dd = isns_dd_clone(temp_dd);
++
++		dd->dd_object = isns_object_get(obj);
++
++		isns_dd_insert(dd);
++		isns_dd_add_members(dd, db, temp_dd);
++
++		/* Clear the ddm_added flag for all members, to
++		 * prevent all information from being duplicated
++		 * to the DB on the next DD modification. */
++		for (mp = dd->dd_members; mp; mp = mp->ddm_next)
++			mp->ddm_added = 0;
++
++next:
++		isns_dd_release(temp_dd);
++	}
++
++	isns_object_list_destroy(&list);
++	isns_dd_list_initialized = 1;
++	return ISNS_SUCCESS;
++}
++
++isns_object_t *
++isns_dd_get_member_object(isns_db_t *db, const isns_attr_t *key1,
++		const isns_attr_t *key2,
++		int create)
++{
++	isns_attr_list_t query = ISNS_ATTR_LIST_INIT;
++	isns_object_template_t *tmpl = NULL;
++	isns_object_t	*obj;
++	isns_portal_info_t portal_info;
++	const char	*key_string = NULL;
++	uint32_t	key_index = 0;
++
++	switch (key1->ia_tag_id) {
++	case ISNS_TAG_DD_MEMBER_ISCSI_INDEX:
++		key_index = key1->ia_value.iv_uint32;
++		isns_attr_list_append_uint32(&query,
++				ISNS_TAG_ISCSI_NODE_INDEX,
++				key_index);
++		tmpl = &isns_iscsi_node_template;
++		break;
++
++	case ISNS_TAG_DD_MEMBER_ISCSI_NAME:
++		key_string = key1->ia_value.iv_string;
++		isns_attr_list_append_string(&query,
++				ISNS_TAG_ISCSI_NAME,
++				key_string);
++		tmpl = &isns_iscsi_node_template;
++		break;
++
++	case ISNS_TAG_DD_MEMBER_FC_PORT_NAME:
++		key_string = key1->ia_value.iv_string;
++		isns_attr_list_append_string(&query,
++				ISNS_TAG_FC_PORT_NAME_WWPN,
++				key_string);
++		tmpl = &isns_fc_port_template;
++		break;
++
++	case ISNS_TAG_DD_MEMBER_PORTAL_INDEX:
++		key_index = key1->ia_value.iv_uint32;
++		isns_attr_list_append_uint32(&query,
++				ISNS_TAG_PORTAL_INDEX,
++				key_index);
++		tmpl = &isns_portal_template;
++		break;
++
++	case ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR:
++		if (!isns_portal_from_attr_pair(&portal_info, key1, key2)
++		 || !isns_portal_to_attr_list(&portal_info,
++			 ISNS_TAG_PORTAL_IP_ADDRESS,
++			 ISNS_TAG_PORTAL_TCP_UDP_PORT,
++			 &query))
++			return NULL;
++
++		key_string = isns_portal_string(&portal_info);
++		tmpl = &isns_portal_template;
++		break;
++
++	default:
++		return NULL;
++	}
++
++	obj = isns_db_lookup(db, tmpl, &query);
++	if (!obj && create) {
++		if (!key_string) {
++			isns_debug_state("Attempt to register %s DD member "
++					"with unknown index %u\n",
++					tmpl->iot_name, key_index);
++			goto out;
++		}
++
++		obj = isns_create_object(tmpl, &query, NULL);
++		if (obj != NULL)
++			isns_debug_state("Created limbo object for "
++					"%s DD member %s\n",
++					tmpl->iot_name, key_string);
++	}
++
++out:
++	isns_attr_list_destroy(&query);
++	return obj;
++
++}
+diff --git a/utils/open-isns/deregister.c b/utils/open-isns/deregister.c
+new file mode 100644
+index 0000000..3a7b7a6
+--- /dev/null
++++ b/utils/open-isns/deregister.c
+@@ -0,0 +1,271 @@
++/*
++ * Handle iSNS Device Deregistration
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include "isns.h"
++#include "attrs.h"
++#include "objects.h"
++#include "message.h"
++#include "security.h"
++#include "util.h"
++#include "db.h"
++
++extern isns_source_t *	isns_server_source;
++
++
++/*
++ * Create a registration, and set the source name
++ */
++static isns_simple_t *
++__isns_create_deregistration(isns_source_t *source, const isns_attr_list_t *attrs)
++{
++	isns_simple_t	*simp;
++
++	simp = isns_simple_create(ISNS_DEVICE_DEREGISTER, source, NULL);
++	if (simp && attrs)
++		isns_attr_list_copy(&simp->is_operating_attrs, attrs);
++	return simp;
++}
++
++isns_simple_t *
++isns_create_deregistration(isns_client_t *clnt, const isns_attr_list_t *attrs)
++{
++	return __isns_create_deregistration(clnt->ic_source, attrs);
++}
++
++/*
++ * Get the next object identified by the operating attrs.
++ */
++static int
++isns_deregistration_get_next_object(isns_db_t *db,
++				struct isns_attr_list_scanner *st,
++				isns_object_list_t *result)
++{
++	isns_object_t	*current;
++	int		status;
++
++	status = isns_attr_list_scanner_next(st);
++	if (status)
++		return status;
++
++	/*
++	 * 5.6.5.4.
++	 * Valid Operating Attributes for DevDereg
++	 * ---------------------------------------
++	 *    Entity Identifier
++	 *    Portal IP-Address & Portal TCP/UDP Port
++	 *    Portal Index
++	 *    iSCSI Name
++	 *    iSCSI Index
++	 *    FC Port Name WWPN
++	 *    FC Node Name WWNN
++	 *
++	 * In other words, deregistration is restricted to Entity,
++	 * portal, and node
++	 */
++	if (st->tmpl != &isns_entity_template
++	 && st->tmpl != &isns_iscsi_node_template
++	 && st->tmpl != &isns_portal_template)
++		return ISNS_INVALID_DEREGISTRATION;
++
++	/* Only key attrs allowed */
++	if (st->attrs.ial_count) {
++		/* MS Initiators send the Entity protocol along
++		 * with the Entity Identifier. */
++		isns_debug_protocol("Client included invalid operating attrs "
++				"with %s:\n", st->tmpl->iot_name);
++		isns_attr_list_print(&st->attrs, isns_debug_protocol);
++		/* return ISNS_INVALID_DEREGISTRATION; */
++	}
++
++	/*
++	 * 5.6.5.4
++	 * Attempted deregistration of non-existing entries SHALL not
++	 * be considered an isns_error.
++	 */
++	current = isns_db_lookup(db, st->tmpl, &st->keys);
++	if (current != NULL) {
++		isns_object_list_append(result, current);
++		isns_object_release(current);
++	}
++
++	return ISNS_SUCCESS;
++}
++
++/*
++ * Extract the list of objects to be deregistered from
++ * the list of operating attributes.
++ */
++static int
++isns_deregistration_get_objects(isns_simple_t *reg, isns_db_t *db,
++					isns_object_list_t *result)
++{
++	struct isns_attr_list_scanner state;
++	int		status = ISNS_SUCCESS;
++
++	isns_attr_list_scanner_init(&state, NULL, &reg->is_operating_attrs);
++	state.index_acceptable = 1;
++	state.source = reg->is_source;
++
++	while (state.pos < state.orig_attrs.ial_count) {
++		status = isns_deregistration_get_next_object(db,
++				&state, result);
++
++		if (status == 0)
++			continue;
++
++		/* Translate error codes */
++		if (status == ISNS_NO_SUCH_ENTRY)
++			status = ISNS_SUCCESS;
++		else
++		if (status == ISNS_INVALID_REGISTRATION)
++			status = ISNS_INVALID_DEREGISTRATION;
++		break;
++	}
++
++	isns_attr_list_scanner_destroy(&state);
++	return status;
++}
++
++/*
++ * Process a deregistration
++ *
++ * Normally, you would expect that a deregistration removes the
++ * object from the database, and that's the end of the story.
++ * Unfortunately, someone added Discovery Domains to the protocol,
++ * requiring _some_ information to survive as long as an object
++ * is referenced by a discovery domain. Specifically, we need to
++ * retain the relationship between key attributes (eg iscsi node
++ * name) and the object index.
++ *
++ * Thus, deregistration consists of the following steps
++ *  -	the object is removed from the database's global scope,
++ *	so that it's no longer visible to DB lookups.
++ *
++ *  -	the object is detached from its containing Network
++ *	Entity.
++ *
++ *  -	all attributes except the key attr(s) and the index
++ *	attribute are removed.
++ */
++int
++isns_process_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
++{
++	isns_object_list_t	objects = ISNS_OBJECT_LIST_INIT;
++	isns_simple_t		*reply = NULL;
++	isns_db_t		*db = srv->is_db;
++	int			status, dereg_status;
++	unsigned int		i;
++
++	/* Get the objects to deregister */
++	status = isns_deregistration_get_objects(call, db, &objects);
++	if (status != ISNS_SUCCESS)
++		goto done;
++
++	/*
++	 * 5.6.5.4
++	 *
++	 * For messages that change the contents of the iSNS database,
++	 * the iSNS server MUST verify that the Source Attribute
++	 * identifies either a Control Node or a Storage Node that is
++	 * a part of the Network Entity containing the added, deleted,
++	 * or modified objects.
++	 */
++	/*
++	 * Implementation note: this can be implemented either by
++	 * explicitly checking the object's owner in isns_db_remove
++	 * (which is what we do right now), or by matching only
++	 * those objects that have the right owner anyway.
++	 *
++	 * The latter sounds like a better choice if the client
++	 * uses NIL attributes, because it limits the scope of
++	 * the operation; but then the RFC doesn't say whether
++	 * this kind of deregistration would be valid at all.
++	 */
++
++	/* Success: create a new simple message, and
++	 * send it in our reply. */
++	reply = __isns_create_deregistration(srv->is_source, NULL);
++	if (reply == NULL) {
++		status = ISNS_INTERNAL_ERROR;
++		goto done;
++	}
++
++	dereg_status = ISNS_SUCCESS;
++	for (i = 0; i < objects.iol_count; ++i) {
++		isns_object_t	*obj = objects.iol_data[i];
++
++		/* Policy: check that the client is permitted
++		 * to deregister this object */
++		if (!isns_policy_validate_object_access(call->is_policy,
++					call->is_source, obj,
++					call->is_function))
++			status = ISNS_SOURCE_UNAUTHORIZED;
++
++		if (status == ISNS_SUCCESS)
++			status = isns_db_remove(db, obj);
++		if (status != ISNS_SUCCESS) {
++			/*
++			 * 5.7.5.4
++			 *
++			 * In the event of an error, this response message
++			 * contains the appropriate status code as well
++			 * as a list of objects from the original DevDereg
++			 * message that were not successfully deregistered
++			 * from the iSNS database.  This list of objects
++			 * is contained in the Operating Attributes
++			 * of the DevDeregRsp message.	Note that an
++			 * attempted deregistration of a non-existent
++			 * object does not constitute an isns_error, and
++			 * non-existent entries SHALL not be returned
++			 * in the DevDeregRsp message.
++			 */
++			/*
++			 * Implementation: right now this doesn't work
++			 * at all, because isns_msg_set_error will
++			 * discard the entire message except for the
++			 * status word.
++			 */
++			isns_debug_message("Failed to deregister object: %s (0x%04x)\n",
++				isns_strerror(status), status);
++
++			isns_object_extract_all(obj, &reply->is_operating_attrs);
++			dereg_status = status;
++			continue;
++		}
++
++		/*
++		 * 5.7.5.4
++		 * If all Nodes and Portals associated with a Network
++		 * Entity are deregistered, then the Network Entity
++		 * SHALL also be removed.
++		 * [...]
++		 * If both the Portal and iSCSI Storage Node objects
++		 * associated with a Portal Group object are removed,
++		 * then that Portal Group object SHALL also be removed.
++		 * The Portal Group object SHALL remain registered
++		 * as long as either of its associated Portal or
++		 * iSCSI Storage Node objects remain registered.  If a
++		 * deleted Storage Node or Portal object is subsequently
++		 * re-registered, then a relationship between the re-
++		 * registered object and an existing Portal or Storage
++		 * Node object registration, indicated by the PG object,
++		 * SHALL be restored.
++		 */
++		/* isns_db_remove takes care of removing dead entities,
++		 * and dead portal groups.
++		 */
++	}
++
++	if (status == ISNS_SUCCESS)
++		status = dereg_status;
++
++done:
++	isns_object_list_destroy(&objects);
++	*result = reply;
++	return status;
++}
+diff --git a/utils/open-isns/doc/isns_config.5 b/utils/open-isns/doc/isns_config.5
+new file mode 100644
+index 0000000..5fbd26e
+--- /dev/null
++++ b/utils/open-isns/doc/isns_config.5
+@@ -0,0 +1,387 @@
++.TH ISNS_CONFIG 8 "11 May 2007"
++.SH NAME
++isns_config - iSNS configuration file
++.SH SYNOPSIS
++.B /etc/isns/isnsadm.conf
++.br
++.B /etc/isns/isnsd.conf
++.br
++.B /etc/isns/isnsdd.conf
++
++.SH DESCRIPTION
++All Open-iSNS utilities read their configuration
++from a file in
++.BR /etc/isns .
++There is a separate configuration file for each application,
++.BR isnsd ", " isnsadm ", and " isnsdd .
++The syntax and the set of supported options is identical,
++even though some options are specific to e.g. the server.
++Unless indicated, options are applicable to all utilities.
++.PP
++An Open-iSNS configuration file contains keyword-argument pairs,
++one per line.  All keywords are case insensitive.
++.PP
++A 
++.B #
++character introduces a comment, which extends until the
++end of the line. Empty lines are ignored.
++.PP
++There are no line continuations, and you cannot use quotes
++around arguments.
++.PP
++Some options specify timeout values, which are given in
++units of seconds by default. You can specify an explicit
++unit, however, such as
++.BR d " (days),
++.BR h " (hours),
++.BR m " (minutes), or
++.BR s " (seconds).
++.\" ------------------------------------------------------------------
++.SS Generic Options
++.TP
++.BR HostName
++By default, Open-iSNS applications will retrieve the machine's
++hostname using the
++.BR gethostname (3)
++system call, and use a DNS lookup to look up the canonical name.
++Using the
++.BR HostName
++option, you can overried this. This option is rarely needed.
++.TP
++.BR SourceName
++This option is mandatory for all Open-iSNS applications.
++This should be a name which identifies the client uniquely.
++There are two readings of RFC 4171; one requires that this
++is an iSCSI qualified name such as
++.BR iqn.2001-04.com.example.host ,
++whereas other language in the RFC suggests that this is
++pretty much a free-format string that just has to be
++unique (using e.g. the client's fully qualified domain name).
++.IP
++When using DSA authentication, Open-iSNS currently requires the source
++name to match the key identifier (SPI) of the client's public
++key.
++.IP
++If left empty, the source name is derived from the client's hostname.
++.TP
++.BR ServerAddress " (client):
++This options specifies the host name or address of
++the iSNS server to talk to. It can optionally be followed
++by a colon, and a port number.
++.IP
++Instead of a hostname, IPv4 or IPv6 addresses can be used.
++In order to avoid ambiguities, literal
++IPv6 addresses must be surrounded by square brackets,
++as in
++.BR [2001:4e5f::1] .
++.IP
++When specifying a port number, you can use either the
++numeric port, or a string name to be looked up in
++.BR /etc/services .
++When the port is omitted, it defaults to 3205, the IANA
++assigned port number of iSNS.
++.IP
++If the special string
++.B SLP:
++is used, the client will try to locate the iSNS server
++through SLP.
++.TP
++.BR SLPRegister " (server):
++If set to 1, the iSNS daemon will register itself will
++the SLP service. This allows clients to contact the
++server without having to configure its address
++statically.
++.TP
++.BR PIDFile " (server):
++This specifies the name of the server's PID file, which is
++.B /var/run/isnsd.pid
++by default.
++.\" ------------------------------------------------------------------
++.SS Database Related Options
++These options apply to the iSNS server only, and control operation
++of the iSNS database.
++.TP
++.BR Database
++This option is used to specify how the database is stored.
++Setting this to an absolute path name will make
++.B isnsd
++keep its database in the specified directory.
++.IP
++If you leave this empty,
++.B isnsd
++will keep its database in memory.
++This is also the default setting.
++.TP
++.BR DefaultDiscoveryDomain
++iSNS scopes visibility of other nodes using so-called
++Discovery Domains. A storage node A will only "see"
++storage node B, if both are members of the same
++discovery domain.
++.IP
++So if a storage node is registered which is not part of
++any discovery domain, it will not see any other nodes.
++.IP
++By setting
++.BR DefaultDiscoveryDomain=1 ,
++you can tell isnsd to create a virtual "default discovery domain", which
++holds all nodes that are not part of any administratively configured
++discovery domain.
++.IP
++By default, there is no default discovery domain.
++.TP
++.BR RegistrationPeriod
++The iSNS server can purge registered entities after a certain period
++of inactivity. This is called the registration period.  Clients who
++register objects are supposed to refresh their registration within
++this period.
++.IP
++The default value is 1 hour. Setting it to 0 disables expiry
++of entities from the database.
++.TP
++.BR ESIRetries
++Open-iSNS is able to monitor the reachability of storage nodes
++and their portals by using a protocol feature called ESI
++(Entity status inquiry). Clients request ESI monitoring by
++registering an ESI port along with each portal. The server
++will send ESI messages to these portals at regular intervals.
++If the portal fails to reply several times in a row, it is
++considered dead, and will be removed from the database.
++.IP
++.B ESIRetries
++specifies the maximum number of attempts the server will make
++at contacting the portal before pronouncing it dead. If set
++to 0, the server will disable ESI and reject any registrations
++that specify an ESI port with an error code of "ESI not
++supported".
++.IP
++The default value is 3.
++.TP
++.BR ESIMinInterval
++This timeout value specifies the minimum ESI interval.
++If a client requests an ESI interval less than this value,
++it is silently rounded up.
++.IP
++The default value is 60 seconds.
++.TP
++.BR ESIMaxInterval
++This timeout value specifies the maximum ESI interval.
++If a client requests an ESI interval greater than this value,
++it is silently rounded down.
++.IP
++The default value is 10 minutes.
++.IP
++The maximum ESI interval must not exceed half the value
++of the registration period.
++.TP
++.B SCNRetries
++iSNS clients can register to receive State Change Notification
++(SCN) messages to learn about changes in the iSNS database.
++This value specifies how often the server will try to retransmit
++an SCN message until giving up.
++.IP
++The default value is 3.
++.TP
++.B SCNCallout
++This is the path name of a helper program that
++.B isnsdd
++will invoke whenever it processes a state change notification from the
++server. The helper program will be invoked with an argument indicating
++the type of event, being one of
++.BR add ", " update ", or " remove .
++This is followed by a list of attributes in 
++.IB name = value
++notation, using the names and conventions described in
++.BR isnsadm (8).
++.\" ------------------------------------------------------------------
++.SS Security Related Options
++The iSNS standard defines an authentication method based on
++the DSA algorithm. Participants in a message exchange authenticate
++messages by adding an "authentication block" containing a time stamp,
++a string identifying the key used, and a digital signature of the
++message.  The same method is also used by SLP, the Service Location
++Protocol.
++.PP
++The string contained in the authentication block is referred to
++as the
++.IR "Security Policy Index" (SPI).
++This string can be used by the server to look up the client's public
++key by whatever mechanism; so the string could be used as the name of
++a public key file in a directory, or to retrieve an X509 certificate
++from LDAP.
++.PP
++From the perspective of Open-iSNS client applications, there are
++only two keys: the client's own (private) key, used to sign the
++messages it sends to the server, and the server's public key,
++used to verify the signatures of incoming server messages.
++.PP
++The iSNS server needs, in addition to its own private key, access to all
++public keys of clients that will communicate to it. The latter are kept
++in what is called a key store. Key stores and their operation will
++be discussed in section
++.B Key Stores and Policy
++below.
++.PP
++The following configuration options control authentication:
++.TP
++.BR Security
++This enables or disables DSA authentication.
++When set to 1, the client will sign all messages, and expect all server
++messages to be signed.
++.IP
++When enabling security in the server, incoming messages are checked
++for the presence of an auth block. If none is present, or if the server
++cannot find a public key corresponding to the SPI, the message is treated
++as originating from an anonymous source. If the SPI is known but the
++signature is incorrect, the message is dropped silently.
++.IP
++Messages from an anonymous source will be assigned a very restrictive
++policy that allows database queries only.
++.IP
++Setting this option to 0 will turn off authentication.
++.IP
++The default value is -1, which tells iSNS to use authentication
++if the required keys are installed, and use unauthenticated iSNS
++otherwise.
++.TP
++.BR AuthName
++This is the string that will be used as the SPI in all outgoing
++messages that have an auth block. It defaults to the host name
++(please refer to option
++.BR HostName ).
++.TP
++.BR AuthKeyFile
++This is the path name of a file containing a PEM encoded DSA key.
++This key is used to sign outgoing messages.
++The default is
++.BR /etc/isns/auth_key .
++.TP
++.BR ServerKeyFile
++This option is used by client applications only, and specifies
++the path name of a file containing a PEM encoded DSA key.
++This key is used to authenticate the server's replies.
++The default is
++.BR /etc/isns/server_key.pub .
++.TP
++.BR KeyStore
++This server-side option specifies the key store to use,
++described in the next section.
++.PP
++The following two options control how iSNS will verify the
++time stamp contained in the authentication block, which
++is supposed to prevent replay attacks.
++.TP
++.B Auth.ReplayWindow
++In order to compensate for clock drift between two hosts exchanging
++iSNS messages, Open-iSNS will apply a little fuzz when comparing
++the time stamp contained in the message
++to the local system time. If the difference between
++time stamp and local system time is less than the number of seconds
++given by this option, the message is acceptable. Otherwise, it is
++rejected.
++.IP
++The default value is
++.BR 5m .
++.TP
++.B Auth.TimestampJitter
++When verifying incoming messages, Open-iSNS checks that the time
++stamps sent by the peer are increasing monotonically. In order to
++compensate for the reordering of messages by the network (eg when
++using UDP as transport), a certain time stamp jitter is accepted.
++If the time stamp of an incoming messages is no earlier than
++.B TimestampJitter
++seconds before the last time stamp received, then the message is acceptable.
++Otherwise, it is rejected.
++.IP
++The default value is
++.BR 1s .
++.\" ------------------------------------------------------------------
++.SS Key Stores and Policy
++The current implementation supports two types of key stores.
++.PP
++The simple key store uses a flat directory to store public keys, each
++key in a file of its own. The file is expected to hold the client's
++PEM-encoded public key, and it must use the client's SPI as the name.
++This type of key store is not really recommended, as it does not
++store any policy information.
++.PP
++A simple key store can be configured by setting the
++.B KeyStore
++option to the path name of the directory.
++.PP
++The recommended approach is to use the database as key store. This
++uses vendor-specific policy objects to tie SPI string, public key,
++entity name, source name and other bits of policy together, and
++store them in a persistent way.
++.PP
++The database key store is configured by setting the
++.B KeyStore
++option to the reserved value
++.BR DB: ,
++which is also the default.
++.PP
++Currently, Open-iSNS policy objects have the following attributes,
++besides the SPI:
++.TP
++Source:
++This is the source node name the client must use. It defaults to
++the SPI string.
++.TP
++Functions:
++This is a bitmap detailing which functions the client is permitted
++to invoke. The bit names correspond to the shorthand names used in
++RFC 4711, such as
++.BR DevAttrReg ,
++.BR DevAttrQry ,
++etc. The default is to allow registration, query and deregistration,
++as well as SCNRegister.
++.TP
++Entity name:
++This is the entity name assigned to the client. If set, a registration
++by the client is not permitted to use a different entity name. If
++the client sends a registration without Entity identifier, the
++server will assign the entity name given in the policy.
++The default is to not restrict the entity name.
++.TP
++Object access:
++This is a bitfield describing access permissions for each object type.
++For each object type, you can grant Read and/or Write permissions.
++Read access applies to the Query and GetNext calls; all other operations
++require write permission.
++The default grants read and write access to objects of type Entity, Storage
++Node, Portal and Portal Group; and read access to Discovery Domains.
++.TP
++Node types:
++This bitfield describes which types of storage nodes a client is
++allowed to register; the valid bit names are
++.BR target ", " initiator " and " control .
++The default is to restrict nodes to register initiators only.
++.\" ------------------------------------------------------------------
++.SS Network Related Options
++.TP
++.BR Network.MaxSockets
++This is the number of incoming connections accepted, and defaults to
++1024. This usually applies to server side only, but is relevant if you
++create a passive TCP socket for ESI or SCN.
++.TP
++.BR Network.ConnectTimeout
++This is a timeout value, which specifies the time to wait for a TCP
++connection to be established.  It defaults to
++.BR 60s .
++.TP
++.BR Network.ReconnectTimeout
++When a connection attempt failed, we wait for a short time before we
++try connecting again. This is intended to take the pressure off
++overloaded servers. The default value is
++.BR 10s .
++.TP
++.BR Network.CallTimeout
++Total amount of time to wait before timing out a call to the iSNS server.
++The default value is
++.BR 60s .
++.\" ------------------------------------------------------------------
++.SH SEE ALSO
++RFC 4171,
++.BR isnsd (8),
++.BR isnsadm (8).
++.SH AUTHORS
++Olaf Kirch <olaf.kirch@oracle.com>
+diff --git a/utils/open-isns/doc/isnsadm.8 b/utils/open-isns/doc/isnsadm.8
+new file mode 100644
+index 0000000..88ec4cf
+--- /dev/null
++++ b/utils/open-isns/doc/isnsadm.8
+@@ -0,0 +1,688 @@
++'\" t
++.TH ISNSADM 8 "11 May 2007"
++.SH NAME
++isnsadm \- iSNS client utility
++.SH SYNOPSIS
++.B isnsadm
++.RI [ options... ]
++.RI --register " object...
++.PP
++.B isnsadm
++.RB [ ... ]
++.RI --query " attr" [= value ]
++.PP
++.B isnsadm
++.RB [ ... ]
++.RI --deregister " attr=value
++.PP
++.B isnsadm
++.RB [ ... ]
++.RI --list " type attr=value
++.PP
++.B isnsadm
++.RB [ ... ]
++.RI --dd-register " attr=value
++.PP
++.B isnsadm
++.RB [ ... ]
++.RI --dd-deregister " dd-id attr=value
++.PP
++.B isnsadm
++.RB [ ... ]
++.RI --enroll " client-name attr=value
++.PP
++.B isnsadm
++.RB [ ... ]
++.RI --edit-policy " attr=value
++
++.SH DESCRIPTION
++.B Isnsadm
++is a command line utility for interacting with an iSNS
++server. It operates in one of several modes, which are
++mutually exclusive.
++Currently,
++.B isnsadm
++supports registration, query, and deregistration.
++.SH OPTIONS
++By default,
++.B isnsadm
++will take most of its settings from the configuration
++file
++.BR /etc/isns/isnsadm.conf ,
++with the exception of the following options:
++.TP
++.BI \--config " filename\fR, " \-c " filename
++This option overrides the default configuration file.
++.TP
++.BI \--debug " facility\fR, " \-d " facility
++enables debugging. Valid facilities are
++.PP
++.TS
++tab(,),box,center;
++lb|lr.
++socket,network send/receive
++auth,authentication and security related information
++message,iSNS protocol layer
++state,database state
++scn,SCN (state change notification) messages
++esi,ESI (entity status inquiry) messages
++all,all of the above
++.TE
++.PP
++.TP
++.BI \--local
++makes
++.B isnsadm
++use a Local (aka Unix) socket when talking to the iSNS
++server. This can be used by the administrator to perform
++management tasks, such as enrolling new clients, editing
++access control and so on. Local mode is only available
++to the super user.
++.TP
++.BI \--control
++makes
++.B isnsadm
++assume the identity of a control node. Control nodes are
++special in that they have more rights in accessing and
++modifying the database than normal storage nodes have.
++.PP
++When using this option,
++.B isnsadm
++will use the source name and DSA key specified by the
++.BR Control.SourceName " and " Control.AuthKeyFile 
++configuration options, respectively.
++.PP
++.TP
++.BI \--key " attr" = value
++This option is recognized in registration mode only, and
++lets you specify an object key. For a more detailed explanation,
++refer to section
++.BR "Registration mode" .
++.TP
++.BI \--keyfile= filename
++When creating a policy for a new iSNS client, 
++.B isnsadm
++is able to generate a DSA key for the client. The public
++part of the key is stored in a policy object in the iSNS
++server's database, whereas the private portion is stored in the
++file specified by the
++.B keyfile
++option.
++.B
++.TP
++.BI \--help
++This will print a help message and exit.
++.\"---------------------------
++.SS Built-in help
++.B Isnsadm
++has built-in help functions. When invoked with
++.BR \--help ,
++it will print a general help message showing all supported
++command modes, and exit. Specific help on an individual 
++command mode is available by invoking that mode with a
++single argument of
++.BR help ,
++like this:
++.PP
++.B isnsadm --register help
++.PP
++This will print a help message describing how to use this
++command mode, followed by a list of attributes this command supports
++and a help text describing the attribute.
++.\"---------------------------
++.SS Supported attributes
++Most command modes take a list of attributes as arguments on the
++command line. The naming and syntax of these attributes as
++the same for all commands modes, however certain modes support
++only a limited set of attributes.
++.PP
++Attributes are usually given as
++.IB name = value
++pairs. Where empty (or NIL) attributes are supported, the
++attribute name by itself can be given.
++.PP
++The syntax of attribute
++.I value
++depends on the attribute type. For strings and numeric values,
++no special conventions apply, but bitfields have a special syntax
++described below.
++.PP
++The attribute name is usually preceded by the object
++type it applies to (such as
++.BR entity ),
++followed by a hyphen and the name itself. However, where the
++context clearly determines a specific object type, the prefix
++can be omitted. For instance, when editing a policy object
++using
++.BR \--edit-policy ,
++it is acceptable to use
++.B node-type
++as shorthand for
++.BR policy-node-type .
++.PP
++Likewise, in a query command, it is not permitted to mix attributes
++from different object types. Thus, the first attribute of a
++query string establishes a type context, so that the following
++two invocations are equivalent:
++.PP
++.B isnsadm --query pg-name=iqn.com.foo pg-addr=10.1.1.1 pg-port=860/tcp
++.br
++.B isnsadm --query pg-name=iqn.com.foo addr=10.1.1.1 port=860/tcp
++.PP
++.B Isnsadm
++currently supports the following attributes:
++.PP
++.TS
++tab(,),box,center;
++li|lilili
++lt|lbrlb.
++Context,Attribute,iSNS tag,Aliases
++_
++Network Entity,entity-id,1,eid
++\^,entity-prot,2
++\^,entity-index,7
++iSCSI Storage Node,iscsi-name,32
++\^,iscsi-node-type,33
++\^,iscsi-alias,34
++\^,iscsi-idx,36
++\^,iscsi-authmethod,42
++Portal,portal-addr,16
++\^,portal-port,17
++\^,portal-name,18
++\^,portal-esi-port,20
++\^,portal-esi-interval,21
++\^,portal-idx,22
++\^,portal-scn-port,23
++Portal Group,portal-group-index,52
++\^,pg-name,48
++\^,pg-addr,49
++\^,pg-port,50
++\^,pg-tag,51,pgt
++\^,pg-idx,52
++Discovery Domain,dd-id,2065
++\^,dd-name,2066
++\^,dd-member-iscsi-idx,2067
++\^,dd-member-name,2068
++\^,dd-member-fc-name,2069,
++\^,dd-member-portal-idx,2070,
++\^,dd-member-addr,2071,
++\^,dd-member-port,2072,
++\^,dd-features,2078,
++Policy Object,policy-name,-,spi
++\^,policy-key,-
++\^,policy-entity,-
++\^,policy-node-type,-
++\^,policy-object-type,-
++\^,policy-functions,-
++.TE
++.PP
++.\"---------------------------
++.SS Portal attributes
++Portal information is conveyed by two separate attributes
++in iSNS; an address attribute holding the IP address, and
++a TCP/UDP port attribute holding the port number and an indication
++of the protocol to be used (TCP or UDP).
++.PP
++When parsing a TCP/UDP port, Open-iSNS will expect a port number,
++optionally followed by a slash and the protocol. Port names
++such as "iscsi-target" are not supported.
++.PP
++As a convenience,
++.B isnsadm
++supports a notation representing a portal as one pseudo-attribute.
++Separating address and port by a colon. Thus, the following two
++are equivalent, with the latter being the shorthand representation
++of the former:
++.PP
++.BI addr= <address> " port=" <port> [/ protocol ] \fR.
++.BI portal= <adress> : port [/ protocol ]
++.PP
++This notation can be used in any context where an
++.BR addr / port
++attribute pair can appear, and may be prefixed by a type name,
++as in
++.BR pg-portal=... .
++.PP
++When using literal IPv6 addresses, the address has to be surrounded
++by square brackets, otherwise the embedded colons would create
++ambiguity:
++.BR portal=[2001:5c0:0:2::24]:860/tcp
++.PP
++.\"---------------------------
++.SS Bitfield attributes
++Some iSNS attributes are words representing a bit field.
++.B Isnsadm
++displays and parses these attributes in human-readable form
++rather than using the numerical value. The names of the bit
++values are displayed by built-in help facilities. When specifying
++a bitfield attribute on the command line, you can combine them
++using the plus (\fB+\fP) or comma (\fB,\fR) character, like this:
++.PP
++.B node-type=control+initiator
++.PP
++.\"---------------------------
++.SS Registration mode
++Registration mode is selected by using the
++.B --register
++option, followed by a list of one or more objects
++to register with the iSNS server.
++By default, this will create a network entity for the
++client (if none exists), and place the new objects inside
++it.  Usually, you register all objects for
++a network entity in one operation, rather than each
++one separately.
++.PP
++Each object is specified as a type, optionally followed
++by a comma-separated list of attributes, such as
++this:
++.PP
++.B target=iqn.2005-01.org.open-iscsi.foo:disk1,alias=disk1
++.PP
++The following object types are currently supported:
++.TP
++.BI entity= name
++Tells the server to group all objects in the specified
++Network Entity container object.
++Normally, the iSNS server will automatically assign an
++entity name that is in line with its policies, and there is
++no need to specify it explicitly.
++.TP
++.BI initiator[= name ]
++This will register an iSCSI storage node of type initiator.
++By default, the name is set to the iSNS source name.
++.IP
++This can be followed by any number of iSCSI storage node
++attributes.
++.TP
++.BI target[= name ]
++This will register an iSCSI storage node of type target.
++By default, the name is set to the iSNS source name.
++.IP
++This object accepts the same set of attributes as
++.BR initiator .
++.TP
++.BI control[= name ]
++This will register an iSCSI storage node of type control.
++By default, the name is set to the iSNS source name.
++Only management nodes should be registered as control
++nodes, as this gives a node complete control over the
++iSNS database.
++.IP
++This object accepts the same set of attributes as
++.BR initiator .
++.TP
++.BI portal=[ address:port/proto ]
++This will register a portal using the given address,
++port and protocol triple. If the triple is omitted,
++.B isnsadm
++will use the client host's IP address. If the portal
++is preceded by an initiator registration (on the command
++line), the port defaults to 860/tcp; if it is preceded by
++a target registration, the port defaults to 3260/tcp.
++For multi-homed hosts, the choice of address is
++implementation dependant.
++.IP
++This can be followed by any number of portal attributes.
++.TP
++.B pg
++This will register a portal group joining the preceding
++portal and node. Portal groups can be used to describe
++the preferred portals for a given node; please refer
++to RFC 4711 for details.
++.IP
++This can be followed by any number of portal group attributes.
++The attribute list must specify a portal group tag (PGT)
++via the 
++.BR pgt
++attribute.
++.PP
++There are two additional command line options of interest,
++which are used exclusively with Registration mode. One is
++.BR \--replace .
++Normally, registration mode will
++.I add
++new objects to the network entity associated with the client
++host. If you specify
++.B \--replace
++on the command line, the server will wipe the network
++entity completely, and remove all portals and storage
++nodes it contained. Then it will create a new network
++entity, and place the portals and storage nodes provided
++by the caller inside.
++.PP
++In addition, it is possible to replace just parts of a
++network entity. This is achieved by using the command line
++option
++.B \--key
++to specify the object that should be replaced.
++.PP
++For instance, assume a network entity
++contains the portal 
++.BR 10.1.1.1:860 ,
++and the client's network address changed to
++.BR 10.2.7.7 .
++Then the following command will atomically update the
++database, replacing just the portal without touching the
++registered storage nodes:
++.PP
++.B "  isnsadm --replace --key portal=10.1.1.1:860 portal=10.2.7.7:860
++.PP
++The
++.B \--key
++option recognizes only a subset of the usual attributes:
++.RS
++.TS
++tab(,),box;
++li|li
++lb|lb.
++Object type,Syntax
++_
++Entity,eid=\fIidentifier
++Portal,portal=\fIaddress\fP:\fPport
++iSCSI Node,iscsi-name=\fIname
++.TE
++.RE
++.PP
++To get a list of supported attributes, invoke
++.BR "isnsadm --register help" .
++.\"---------------------------
++.SS Query mode
++Query mode is selected by using the
++.B --query
++option. A query consists of a list of 
++.BR attr = \fI value
++pairs. All attributes must belong to the same object type,
++i.e. queries that mix a Network Entity attribute with e.g.
++a Portal attribute will be rejected.
++.PP
++It is also possible to specify an attribute name without
++value (i.e. just
++.BR attr ),
++which will
++will match any object that has such an attribute, regardless
++of its value. This is useful when you want to query for all
++objects of a given type.
++.PP
++To obtain a list of supported attributes, invoke
++.BR "isnsadm --query help" .
++.\"---------------------------
++.SS List Mode
++In this mode,
++.B isnsadm
++will display all objects of a given type, optionally
++restricted to those matching certain attribute values.
++.PP
++The arguments to list mode are a
++.IR "type name" ,
++optionally followed by one or more 
++.IB attr = value
++pairs. Only attributes pertaining to the given
++type are permitted; for instance, if you specify a
++type name of
++.BR portals ,
++only portal attributes are permitted.
++.PP
++Possible type names are:
++.BR entities ,
++.BR nodes ,
++.BR portals ,
++.BR dds ,
++.BR ddsets ,
++.BR portal-groups ", and "
++.BR policies .
++.PP
++Additional information is available via
++.BR "isnsadm --list help" .
++.\"---------------------------
++.SS Deregistration mode
++In this mode, you can deregister objects previously registered.
++Only the node which registered an entity in the first place is
++permitted to remove it, or any of its child objects. (Control
++nodes are not bound by this restriction).
++.PP
++In deregistration mode, the argument list consists of a list of
++.IB attr = value
++pairs. Deregistration supports the same set of attributes as
++query mode.
++.\"---------------------------
++.SS Discovery Domain Registration
++This mode, allows to register a discovery domain or to add 
++new members to an existing discovery domain. Again, attributes
++are specified as a list of
++.IB attr = value
++pairs. Only discovery domain attributes are recognized.
++.PP
++Note, in order to add members to an existing domain, you must
++specify the domain's numeric ID. The domain's symbolic name
++is not a valid handle when referring to a discovery domain.
++.\"---------------------------
++.SS Discovery Domain Deregistration mode
++In this mode, you can deregister a discoery domain previously registered.
++Only the node which registered a discovery domain in the first place is
++permitted to remove it, or any of its members. (Control
++nodes are not bound by this restriction).
++.PP
++In Discovery Domain deregistration mode, the argument list consists of
++the Discovery Domain ID, followed by a list of
++.IB attr = value
++pairs. Discovery Domain Deregistration supports the same set of attributes as
++query mode.
++.\"---------------------------
++.SS Client Enrollment
++This mode only works when the server recognizes the client
++as having control node capabilities, which is possible in
++two ways:
++.TP
++Invoke
++.B isnsadm \--local
++as super user on the host 
++.B isnsd
++is running on. The
++.B \--local
++options tells it to communicate with the server through
++the local control socket.
++.TP
++Invoke
++.BR "isnsadm \--control" ,
++which tells it to assume the identity of a control node.
++When given this option,
++.B isnsadm
++will use the source name and DSA key specified by the
++.BR Control.SourceName " and " Control.AuthKeyFile 
++configuration options, respectively.
++The server must be configured to grant this identity
++control node status.
++.PP
++To enroll a client, use the
++.B \--enroll
++option, followed by the (source) name of the client to enroll.
++This string will be used as the name of the security policy
++the client will use to identify itself.
++.PP
++This is followed by a list of attribute/value pairs, where the
++following set of attributes is supported:
++.PP
++.TS
++tab(,),box,center;
++li|lilili
++lb|lrlb.
++Attribute,Description,Aliases
++_
++name,Policy Name,spi
++key,Client's DSA public key
++entity,Assigned Entity Identifier
++node-type,Permitted node type(s)
++node-name,Permitted node name(s)
++functions,Bitmap of permitted functions
++object-type,Object access mask
++.TE
++.PP
++The
++.B key
++attribute is used to specify the DSA
++public key that the server should use to authenticate
++messages from this client. You can either provide a
++file name; in which case
++.B isnsadm
++will try to read the PEM encoded public key from that file.
++If no
++.B key
++attribute is given, or when using
++.BR key=gen ", " isnsadm
++will generate a DSA key. The private portion of the newly
++generated key will be stored in the file specified by
++.BI --keyfile= filename \fR.
++.PP
++The
++.B object-type
++attribute is used to specify which object types the client
++is permitted to access. This is a comma separated list of
++.IB type : perm
++pairs, where
++.I type
++can be any of 
++.BR entity ", " iscsi-node ", " portal ", " portal-group ", " dd ", " ddset ", and " policy .
++The permissions can be either 
++.BR rw ", or " r .
++.PP
++The
++.B functions
++attribute can be used to restrict which functions the client is
++permitted to invoke. This is a bitfield, using the standard function
++names from RFC 4171, such as
++.BR DevAttrReg ", " DevAttrQry ", etc."
++.PP
++For a description of the open-isns security model
++and policies, please refer to the
++.BR isns_config (5)
++manual page.
++.PP
++.BR "Important note" :
++In order to generate a DSA key, you have to have a set of DSA
++parameters installed. By default,
++.B isnsadm
++expects to find them in
++.BR /etc/isns/dsa.params .
++These parameters are created by calling
++.B isnsd \--init
++once on the server machine. Alternatively, you can use
++the following command:
++.PP
++.ti +8
++openssl dsaparam 1024 -out /etc/isns/dsa.params
++.ti -8
++.PP
++where 1024 is the chosen DSA key size, in bits.
++.SH EXAMPLES
++If you want to use Open-iSNS in authenticated mode,
++you first need to initialize the server's DSA key and
++DSA parameters. This can be done conveniently by using
++.PP
++.B isnsd --init
++.PP
++This will create the server's private and public key,
++and place them in
++.B /etc/isns/auth_key
++and
++.BR auth_key.pub ,
++respectively.
++.PP
++The following command will create a policy object for a
++node named 
++.B isns.control ,
++and grant it control privileges:
++.PP
++.B isnsadm --local --keyfile=control.key
++.B --enroll isns.control \(rs
++.br
++.B "           node-type=ALL functions=ALL object-type=ALL
++.PP
++In the process of entrolling the client, this will generate
++a DSA key pair, and place the private key portion in the
++file
++.BR control.key .
++This file must be installed as
++.BR /etc/isns/control.key
++on the host you wish to use as an iSNS management station.
++.PP
++Next, you need to create a storage node object for the
++management station:
++.PP
++.B isnsadm --local --register control
++.PP
++On the management station, you can then enroll additional
++hosts:
++.PP
++.B isnsadm --control --keyfile=somehost.key
++.B --enroll iqn.2005-01.org.open-iscsi.somehost \(rs
++.br
++.B "           node-type=target+initiator
++.PP
++Again, this will generate a DSA key pair and store the private
++key portion in auth_key. Note the use of the
++.B \--control
++option that tells 
++.B isnsadm
++to use the identity of the control node instead of the default
++key and source name.
++.PP
++You then need to copy
++.B somehost.key
++to the client host and install it as
++.BR /etc/isns/auth_key .
++Likewise, the server's public key (which resides in
++.BR /etc/isns/auth_key.pub 
++on the server) needs to be copied to the client machine,
++and placed in
++.BR /etc/isns/server_key.pub .
++.PP
++By default, when a client registers a storage node (be
++it initiator or target) with iSNS, the client will not be
++able to see any other storage nodes. In order for targets
++to be visible to a given initiator, you need to create
++so-called Discovery Domains (or DDs for short).
++.PP
++Currently, domain membership operations require administrator
++privilege. Future extensions may allow iSNS clients to
++add themselves to one or more DDs upon registration.
++.PP
++To create a discovery domain, and add nodes to it, you can
++use
++.PP
++.B isnsadm --control --dd-register dd-name=mydomain \(rs
++.br
++.B "           member-name=iqn.org.bozo.client iqn.org.bozo.jbod ...
++.PP
++In order to add members to an existing DD, you have to
++specify the numeric domain ID - using the DD name is not
++sufficient, unfortunately (this is a requirement of the
++RFC, not an implementation issue):
++.PP
++.B isnsadm --control --dd-register dd-id=42 \(rs
++.br
++.B "           member-name=iqn.com.foo member-name=iqn.com.bar
++.PP
++The DD ID can be obtained by doing a query for the DD name:
++.PP
++.B isnsadm --control --query dd-name=mydomain
++.PP
++In management mode, you can also register and deregister
++nodes and portals manually, in case you want to fix up
++an inconsisteny in the database. For instance, this will
++register a node and portal on a host named client.bozo.org:
++.PP
++.B isnsadm --control --register entity=client.bozo.org \(rs
++.br
++.B "           initiator=iqn.org.bozo.client portal=191.168.7.1:860
++.PP
++Note that this registration explicitly specifies the network
++entity in which to place the new objects. If you omit this,
++the new objects will be placed in an entity named 
++.BR CONTROL ,
++which is decidedly not what you want.
++.SH SEE ALSO
++RFC 4171,
++.BR isnsd (8),
++.BR isns_config (5).
++.SH AUTHORS
++Olaf Kirch <olaf.kirch@oracle.com>
+diff --git a/utils/open-isns/doc/isnsd.8 b/utils/open-isns/doc/isnsd.8
+new file mode 100644
+index 0000000..84b3913
+--- /dev/null
++++ b/utils/open-isns/doc/isnsd.8
+@@ -0,0 +1,93 @@
++.TH ISNSD 8 "11 May 2007"
++.SH NAME
++isnsd \- iSNS server daemon
++.SH SYNOPSIS
++.B isnsd
++.RB [ "\-f" ]
++.RB [ "\-4" ]
++.RB [ "\-6" ]
++.RB [ "\-c \fIfilename" ]
++.RB [ "\-d \fIdebug-facility" ]
++.RB [ \--dump-db ]
++.RB [ \--init ]
++
++.SH DESCRIPTION
++.B Isnsd
++implements the iSNS protocol as defined in RFC 4171.
++iSNS is a discovery protocol for iSCSI and iFCP.
++.SH OPTIONS
++By default,
++.B isnsd
++will take most of its settings from the configuration
++file
++.BR /etc/isns/isnsd.conf ,
++with the exception of the following options:
++.TP
++.BI \--config " filename\fR, " \-c " filename
++This option overrides the default configuration file.
++.TP
++.BR \--foreground , \-f
++By default,
++.B isnsd
++will put itself into the background. By specifying this option, you can
++tell it to run in the foreground. Any error messages or debug output
++will be printed to the console rather than being sent to syslog.
++.TP
++.BI \-4
++tells
++.B isnsd
++to create an IPv4 socket only. Normally, it defaults
++to IPv6 (which will accept both IPv4 and IPv6 connections).
++.TP
++.BI \-6
++tells
++.B isnsd
++explicitly
++to create an IPv6 socket only. Since it defaults
++to IPv6 anyway, this is really a no-op.
++.TP
++.BI \--debug " facility\fR, " \-d " facility
++enables debugging. Valid facilities are
++.PP
++.TS
++tab(,),box,center;
++lb|lr.
++socket,network send/receive
++auth,authentication and security related information
++message,iSNS protocol layer
++state,database state
++scn,SCN (state change notification) messages
++esi,ESI (entity status inquiry) messages
++all,all of the above
++.TE
++.PP
++.TP
++.B \--dump-db
++This is a helper function that will read the database from the
++file system, and display it in human readable form. When using
++this option,
++.B isnsd
++will not open any sockets, and terminate immediately after display
++the database.
++.IP
++This option is intended to be used by the administrator when suspecting
++that the database contains bad/inconsistent information.
++.TP
++.B \--init
++This option will create the server's authentication key, and
++the required DSA parameters. The private key is stored in the
++file specified by the
++.B AuthKey
++option (usually
++.BR /etc/isns/auth_key ).
++The public portion of the key is written to same directory,
++with the suffix
++.B .pub
++appended to the key file name.
++.SH SEE ALSO
++RFC 4171,
++.BR isnsadm (8),
++.BR isnsdd (8),
++.BR isns_config (5).
++.SH AUTHORS
++Olaf Kirch <olaf.kirch@oracle.com>
+diff --git a/utils/open-isns/doc/isnsdd.8 b/utils/open-isns/doc/isnsdd.8
+new file mode 100644
+index 0000000..6088e28
+--- /dev/null
++++ b/utils/open-isns/doc/isnsdd.8
+@@ -0,0 +1,75 @@
++.TH ISNSDD 8 "11 May 2007"
++.SH NAME
++isnsdd \- iSNS discovery daemon
++.SH SYNOPSIS
++.B isnsdd
++.RB [ "\-f" ]
++.RB [ "\-c \fIfilename" ]
++.RB [ "\-d \fIdebug-facility" ]
++
++.SH DESCRIPTION
++.B Isnsdd
++is a client side daemon for iSNS. It registers storage
++nodes and portals with the iSNS service, and refreshes
++these registrations in a timely manner.
++.PP
++The daemon also registers itself to receive SCN notifications,
++and processes these. It can be configured to invoke an
++external helper application for each status notification
++received. The path name of the helper application can be
++specified via the
++.B SCNCallout
++option in the configuration file.
++.SH OPTIONS
++By default,
++.B isnsd
++will take most of its settings from the configuration
++file
++.BR /etc/isns/isnsdd.conf ,
++with the addition of the following command line options:
++.TP
++.BI \--config " filename\fR, " \-c " filename
++This option overrides the default configuration file.
++.TP
++.BR \--foreground , \-f
++By default,
++.B isnsd
++will put itself into the background. By specifying this option, you can
++tell it to run in the foreground. Any error messages or debug output
++will be printed to the console rather than being sent to syslog.
++.TP
++.BI \--role " role
++This tells the discovery daemon in which capacity is should register itself
++with the iSNS server.
++.I Role
++can be either
++.BR initiator ", or " control .
++The default is to register as an initiator.
++.IP
++Registering target nodes needs to use a different mechanism, as
++the iSCSI target server needs to inform the discovery daemon
++about each exported target separately. This is not implemented
++yet.
++.TP
++.BI \--debug " facility\fR, " \-d " facility
++enables debugging. Valid facilities are
++.PP
++.TS
++tab(,),box,center;
++lb|lr.
++socket,network send/receive
++auth,authentication and security related information
++message,iSNS protocol layer
++state,database state
++scn,SCN (state change notification) messages
++esi,ESI (entity status inquiry) messages
++all,all of the above
++.TE
++.PP
++.SH SEE ALSO
++RFC 4171,
++.BR isnsd (8),
++.BR isnsadm (8),
++.BR isns_config (5).
++.SH AUTHORS
++Olaf Kirch <olaf.kirch@oracle.com>
+diff --git a/utils/open-isns/domain.c b/utils/open-isns/domain.c
+new file mode 100644
+index 0000000..3b848ac
+--- /dev/null
++++ b/utils/open-isns/domain.c
+@@ -0,0 +1,208 @@
++/*
++ * iSNS object model - discovery domain specific code
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include "isns.h"
++#include "objects.h"
++#include "util.h"
++
++static int
++__isns_default_dd_rebuild(isns_object_t *obj, isns_db_t *db)
++{
++	isns_object_list_t list = ISNS_OBJECT_LIST_INIT;
++	unsigned int	i;
++
++	isns_object_prune_attrs(obj);
++
++	isns_db_get_domainless(db, &isns_iscsi_node_template, &list);
++	for (i = 0; i < list.iol_count; ++i) {
++		isns_object_t	*node = list.iol_data[i];
++		const char	*name;
++		uint32_t	type;
++
++		if (!isns_object_get_uint32(node,
++				ISNS_TAG_ISCSI_NODE_TYPE,
++				&type))
++			continue;
++		if (type & ISNS_ISCSI_CONTROL_MASK)
++			continue;
++		if (!isns_object_get_string(node,
++				ISNS_TAG_ISCSI_NAME,
++				&name))
++			continue;
++		isns_object_set_string(obj,
++				ISNS_TAG_DD_MEMBER_ISCSI_NAME,
++				name);
++	}
++
++	return ISNS_SUCCESS;
++}
++
++/*
++ * Create the default domain
++ */
++isns_object_t *
++isns_create_default_domain(void)
++{
++	isns_object_t	*obj;
++
++	obj = isns_create_object(&isns_dd_template, NULL, NULL);
++	if (!obj)
++		return NULL;
++
++	isns_object_set_uint32(obj, ISNS_TAG_DD_ID, 0);
++	obj->ie_rebuild = __isns_default_dd_rebuild;
++	return obj;
++}
++
++/*
++ * Check object type
++ */
++int
++isns_object_is_dd(const isns_object_t *obj)
++{
++	return ISNS_IS_DD(obj);
++}
++
++int
++isns_object_is_ddset(const isns_object_t *obj)
++{
++	return ISNS_IS_DDSET(obj);
++}
++
++/*
++ * Keep track of DD membership through a bit vector
++ */
++int
++isns_object_mark_membership(isns_object_t *obj, uint32_t id)
++{
++	if (!obj->ie_membership)
++		obj->ie_membership = isns_bitvector_alloc();
++
++	return isns_bitvector_set_bit(obj->ie_membership, id);
++}
++
++int
++isns_object_test_membership(const isns_object_t *obj, uint32_t id)
++{
++	if (!obj->ie_membership)
++		return 0;
++
++	return isns_bitvector_test_bit(obj->ie_membership, id);
++}
++
++int
++isns_object_clear_membership(isns_object_t *obj, uint32_t id)
++{
++	if (!obj->ie_membership)
++		return 0;
++
++	return isns_bitvector_clear_bit(obj->ie_membership, id);
++}
++
++/*
++ * Check whether the two objects share a discovery domain,
++ * and if so, return the DD_ID.
++ * Returns -1 otherwise.
++ */
++int
++isns_object_test_visibility(const isns_object_t *a, const isns_object_t *b)
++{
++	/* The admin can tell isnsd to put all nodes which are *not*
++	 * in any discovery domain, into the so-called default domain */
++	if (isns_config.ic_use_default_domain
++	 && a->ie_template == b->ie_template
++	 && isns_bitvector_is_empty(a->ie_membership)
++	 && isns_bitvector_is_empty(b->ie_membership))
++		return 1;
++
++	return isns_bitvector_intersect(a->ie_membership, b->ie_membership, NULL) >= 0;
++}
++
++/*
++ * Return all visible nodes and portals
++ */
++static int
++__isns_object_vis_callback(uint32_t dd_id, void *ptr)
++{
++	isns_object_list_t *list = ptr;
++
++	/* Get all active members */
++	isns_dd_get_members(dd_id, list, 1);
++	return 0;
++}
++
++void
++isns_object_get_visible(const isns_object_t *obj,
++			isns_db_t *db,
++			isns_object_list_t *result)
++{
++	if (isns_bitvector_is_empty(obj->ie_membership)) {
++		/* Get all other nodes not in any DD */
++		if (isns_config.ic_use_default_domain)
++			isns_db_get_domainless(db,
++					obj->ie_template,
++					result);
++		return;
++	}
++
++	isns_bitvector_foreach(obj->ie_membership,
++			__isns_object_vis_callback,
++			result);
++}
++
++/*
++ * Object templates
++ */
++static uint32_t discovery_domain_attrs[] = {
++	ISNS_TAG_DD_ID,
++	ISNS_TAG_DD_SYMBOLIC_NAME,
++	ISNS_TAG_DD_MEMBER_ISCSI_INDEX,
++	ISNS_TAG_DD_MEMBER_ISCSI_NAME,
++	ISNS_TAG_DD_MEMBER_FC_PORT_NAME,
++	ISNS_TAG_DD_MEMBER_PORTAL_INDEX,
++	ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR,
++	ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT,
++	ISNS_TAG_DD_FEATURES,
++};
++
++static uint32_t discovery_domain_key_attrs[] = {
++	ISNS_TAG_DD_ID,
++};
++
++isns_object_template_t		isns_dd_template = {
++	.iot_name	= "Discovery Domain",
++	.iot_handle	= ISNS_OBJECT_TYPE_DD,
++	.iot_attrs	= discovery_domain_attrs,
++	.iot_num_attrs	= array_num_elements(discovery_domain_attrs),
++	.iot_keys	= discovery_domain_key_attrs,
++	.iot_num_keys	= array_num_elements(discovery_domain_key_attrs),
++	.iot_index	= ISNS_TAG_DD_ID,
++	.iot_next_index	= ISNS_TAG_DD_NEXT_ID,
++};
++
++static uint32_t dd_set_attrs[] = {
++	ISNS_TAG_DD_SET_ID,
++	ISNS_TAG_DD_SET_SYMBOLIC_NAME,
++	ISNS_TAG_DD_SET_STATUS,
++};
++
++static uint32_t dd_set_key_attrs[] = {
++	ISNS_TAG_DD_SET_ID,
++};
++
++isns_object_template_t		isns_ddset_template = {
++	.iot_name	= "Discovery Domain Set",
++	.iot_handle	= ISNS_OBJECT_TYPE_DDSET,
++	.iot_attrs	= dd_set_attrs,
++	.iot_num_attrs	= array_num_elements(dd_set_attrs),
++	.iot_keys	= dd_set_key_attrs,
++	.iot_num_keys	= array_num_elements(dd_set_key_attrs),
++	.iot_next_index	= ISNS_TAG_DD_SET_NEXT_ID,
++};
++
+diff --git a/utils/open-isns/entity.c b/utils/open-isns/entity.c
+new file mode 100644
+index 0000000..cd45e1f
+--- /dev/null
++++ b/utils/open-isns/entity.c
+@@ -0,0 +1,127 @@
++/*
++ * iSNS object model - network entity specific code
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include "isns.h"
++#include "objects.h"
++#include "util.h"
++
++/*
++ * Create a network entity
++ */
++isns_object_t *
++isns_create_entity(int protocol, const char *name)
++{
++	isns_object_t	*obj;
++
++	obj = isns_create_object(&isns_entity_template, NULL, NULL);
++	isns_object_set_string(obj,
++			ISNS_TAG_ENTITY_IDENTIFIER,
++			name);
++	isns_object_set_uint32(obj,
++			ISNS_TAG_ENTITY_PROTOCOL,
++			protocol);
++
++	return obj;
++}
++
++isns_object_t *
++isns_create_entity_for_source(const isns_source_t *source,
++		const char *eid)
++{
++	switch (isns_source_type(source)) {
++	case ISNS_TAG_ISCSI_NAME:
++		return isns_create_entity(ISNS_ENTITY_PROTOCOL_ISCSI, eid);
++
++	case ISNS_TAG_FC_PORT_NAME_WWPN:
++		return isns_create_entity(ISNS_ENTITY_PROTOCOL_IFCP, eid);
++	}
++
++	return NULL;
++}
++
++const char *
++isns_entity_name(const isns_object_t *node)
++{
++	const isns_attr_t *attr;
++
++	if (node->ie_attrs.ial_count == 0)
++		return NULL;
++	attr = node->ie_attrs.ial_data[0];
++	if (attr->ia_value.iv_type != &isns_attr_type_string
++	 || attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER)
++		return NULL;
++
++	return attr->ia_value.iv_string;
++
++}
++
++int
++isns_object_is_entity(const isns_object_t *obj)
++{
++	return ISNS_IS_ENTITY(obj);
++}
++
++/*
++ * 6.2.4.  Entity Registration Timestamp
++ *
++ * This field indicates the most recent time when the Network Entity
++ * registration occurred or when an associated object attribute was
++ * updated or queried by the iSNS client registering the Network Entity.
++ * The time format is, in seconds, the update period since the standard
++ * base time of 00:00:00 GMT on January 1, 1970.  This field cannot be
++ * explicitly registered.  This timestamp TLV format is also used in
++ * the SCN and ESI messages.
++ *
++ * Implementer's note: we consider any kind of activity from
++ * the client an indication that it is still alive.
++ * Only exception is the pseudo-entity that holds the access control
++ * information; we never assign it a timestamp so it is never subject
++ * to expiry.
++ */
++void
++isns_entity_touch(isns_object_t *obj)
++{
++	/* Do not add a timestamp to entity CONTROL */
++	if (obj == NULL
++	 || (obj->ie_flags & ISNS_OBJECT_PRIVATE)
++	 || obj->ie_template != &isns_entity_template)
++		return;
++	isns_object_set_uint64(obj, ISNS_TAG_TIMESTAMP, time(NULL));
++}
++
++/*
++ * Object template
++ */
++static uint32_t entity_attrs[] = {
++	ISNS_TAG_ENTITY_IDENTIFIER,
++	ISNS_TAG_ENTITY_PROTOCOL,
++	ISNS_TAG_MGMT_IP_ADDRESS,
++	ISNS_TAG_TIMESTAMP,
++	ISNS_TAG_PROTOCOL_VERSION_RANGE,
++	ISNS_TAG_REGISTRATION_PERIOD,
++	ISNS_TAG_ENTITY_INDEX,
++	ISNS_TAG_ENTITY_ISAKMP_PHASE_1,
++	ISNS_TAG_ENTITY_CERTIFICATE,
++};
++
++static uint32_t	entity_key_attrs[] = {
++	ISNS_TAG_ENTITY_IDENTIFIER,
++};
++
++isns_object_template_t		isns_entity_template = {
++	.iot_name	= "Network Entity",
++	.iot_handle	= ISNS_OBJECT_TYPE_ENTITY,
++	.iot_attrs	= entity_attrs,
++	.iot_num_attrs	= array_num_elements(entity_attrs),
++	.iot_keys	= entity_key_attrs,
++	.iot_num_keys	= array_num_elements(entity_key_attrs),
++	.iot_index	= ISNS_TAG_ENTITY_INDEX,
++	.iot_next_index	= ISNS_TAG_ENTITY_NEXT_INDEX,
++};
++
+diff --git a/utils/open-isns/error.c b/utils/open-isns/error.c
+new file mode 100644
+index 0000000..0d365e8
+--- /dev/null
++++ b/utils/open-isns/error.c
+@@ -0,0 +1,65 @@
++/*
++ * iSNS error strings etc.
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include "isns.h"
++
++const char *
++isns_strerror(enum isns_status status)
++{
++	switch (status) {
++	case ISNS_SUCCESS:
++		return "Success";
++	case ISNS_UNKNOWN_ERROR:
++		return "Unknown error";
++	case ISNS_MESSAGE_FORMAT_ERROR:
++		return "Message format error";
++	case ISNS_INVALID_REGISTRATION:
++		return "Invalid registration";
++	case ISNS_INVALID_QUERY:
++		return "Invalid query";
++	case ISNS_SOURCE_UNKNOWN:
++		return "Source unknown";
++	case ISNS_SOURCE_ABSENT:
++		return "Source absent";
++	case ISNS_SOURCE_UNAUTHORIZED:
++		return "Source unauthorized";
++	case ISNS_NO_SUCH_ENTRY:
++		return "No such entry";
++	case ISNS_VERSION_NOT_SUPPORTED:
++		return "Version not supported";
++	case ISNS_INTERNAL_ERROR:
++		return "Internal error";
++	case ISNS_BUSY:
++		return "Busy";
++	case ISNS_OPTION_NOT_UNDERSTOOD:
++		return "Option not understood";
++	case ISNS_INVALID_UPDATE:
++		return "Invalid update";
++	case ISNS_MESSAGE_NOT_SUPPORTED:
++		return "Message not supported";
++	case ISNS_SCN_EVENT_REJECTED:
++		return "SCN event rejected";
++	case ISNS_SCN_REGISTRATION_REJECTED:
++		return "SCN registration rejected";
++	case ISNS_ATTRIBUTE_NOT_IMPLEMENTED:
++		return "Attribute not implemented";
++	case ISNS_FC_DOMAIN_ID_NOT_AVAILABLE:
++		return "FC domain id not available";
++	case ISNS_FC_DOMAIN_ID_NOT_ALLOCATED:
++		return "FC domain id not allocated";
++	case ISNS_ESI_NOT_AVAILABLE:
++		return "ESI not available";
++	case ISNS_INVALID_DEREGISTRATION:
++		return "Invalid deregistration";
++	case ISNS_REGISTRATION_FEATURE_NOT_SUPPORTED:
++		return "Registration feature not supported";
++	default:
++		break;
++	}
++
++	return "Unknown iSNS status code";
++}
++
+diff --git a/utils/open-isns/esi.c b/utils/open-isns/esi.c
+new file mode 100644
+index 0000000..47d52c6
+--- /dev/null
++++ b/utils/open-isns/esi.c
+@@ -0,0 +1,576 @@
++/*
++ * Handle ESI events
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include "isns.h"
++#include "attrs.h"
++#include "objects.h"
++#include "message.h"
++#include "security.h"
++#include "util.h"
++#include "db.h"
++
++#define ESI_RETRANS_TIMEOUT	60
++
++typedef struct isns_esi isns_esi_t;
++typedef struct isns_esi_portal isns_esi_portal_t;
++
++struct isns_esi {
++	isns_list_t		esi_list;
++	isns_object_t *		esi_object;
++	isns_list_t		esi_portals;
++
++	unsigned int		esi_update : 1;
++};
++
++struct isns_esi_portal {
++	isns_list_t		esp_list;
++	isns_object_t *		esp_object;
++	isns_portal_info_t	esp_portal;
++	unsigned int		esp_interval;
++	isns_portal_info_t	esp_dest;
++
++	isns_socket_t *		esp_socket;
++	unsigned int		esp_retries;
++	unsigned int		esp_timeout;
++	time_t			esp_start;
++	time_t			esp_next_xmit;
++	uint32_t		esp_xid;
++};
++
++int			isns_esi_enabled = 0;
++static isns_server_t *	isns_esi_server = NULL;
++static ISNS_LIST_DECLARE(isns_esi_list);
++
++static void		isns_esi_transmit(void *);
++static void		isns_esi_sendto(isns_esi_t *, isns_esi_portal_t *);
++static void		isns_process_esi_response(uint32_t, int,
++						  isns_simple_t *);
++static void		isns_esi_disconnect(isns_esi_portal_t *);
++static void		isns_esi_restart(isns_esi_portal_t *);
++static void		isns_esi_drop_portal(isns_esi_portal_t *, isns_db_t *, int);
++static void		isns_esi_drop_entity(isns_esi_t *, isns_db_t *, int);
++static int		isns_esi_update(isns_esi_t *);
++static void		isns_esi_schedule(int);
++static void		isns_esi_callback(const isns_db_event_t *, void *);
++
++void
++isns_esi_init(isns_server_t *srv)
++{
++	if (isns_config.ic_esi_retries == 0) {
++		isns_debug_esi("ESI disabled by administrator\n");
++	} else {
++		unsigned int	max_interval;
++
++		isns_register_callback(isns_esi_callback, NULL);
++		isns_esi_schedule(0);
++
++		max_interval = isns_config.ic_registration_period / 2;
++		if (isns_config.ic_esi_max_interval > max_interval) {
++			isns_warning("Max ESI interval adjusted to %u sec "
++					"to match registration period\n",
++					max_interval);
++			isns_config.ic_esi_max_interval = max_interval;
++			if (isns_config.ic_esi_min_interval > max_interval)
++				isns_config.ic_esi_min_interval = max_interval;
++		}
++		isns_esi_server = srv;
++		isns_esi_enabled = 1;
++	}
++}
++
++/*
++ * Timer callback to send out ESI messages.
++ */
++void
++isns_esi_transmit(void *ptr)
++{
++	isns_db_t	*db = isns_esi_server->is_db;
++	isns_list_t	*esi_pos, *esi_next;
++	time_t		now;
++	isns_object_t	*obj;
++	time_t		next_timeout;
++
++	now = time(NULL);
++	next_timeout = now + 3600;
++
++	isns_list_foreach(&isns_esi_list, esi_pos, esi_next) {
++		isns_list_t	*esp_pos, *esp_next;
++		isns_esi_t	*esi = isns_list_item(isns_esi_t, esi_list, esi_pos);
++
++		if (esi->esi_update) {
++			esi->esi_update = 0;
++			if (!isns_esi_update(esi))
++				continue;
++		}
++
++		isns_list_foreach(&esi->esi_portals, esp_pos, esp_next) {
++			isns_esi_portal_t *esp = isns_list_item(isns_esi_portal_t,
++							esp_list, esp_pos);
++
++			/* Check whether the portal object still exist */
++			obj = esp->esp_object;
++			if (obj->ie_state != ISNS_OBJECT_STATE_MATURE) {
++				isns_esi_drop_portal(esp, db, 0);
++				continue;
++			}
++
++			if (esp->esp_next_xmit <= now) {
++				if (esp->esp_retries == 0) {
++					isns_debug_esi("No ESI response from %s - dropping\n",
++							isns_portal_string(&esp->esp_dest));
++					isns_esi_drop_portal(esp, db, 1);
++					continue;
++				}
++
++				esp->esp_retries -= 1;
++				esp->esp_next_xmit = now + esp->esp_timeout;
++				isns_esi_sendto(esi, esp);
++			}
++			if (esp->esp_next_xmit < next_timeout)
++				next_timeout = esp->esp_next_xmit;
++		}
++
++		if (isns_list_empty(&esi->esi_portals))
++			isns_esi_drop_entity(esi, db, 1);
++	}
++
++	isns_debug_esi("Next ESI message in %d seconds\n", next_timeout - now);
++	isns_esi_schedule(next_timeout - now);
++}
++
++/*
++ * Send an ESI message
++ */
++void
++isns_esi_sendto(isns_esi_t *esi, isns_esi_portal_t *esp)
++{
++	isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
++	isns_socket_t	*sock;
++	isns_simple_t	*msg;
++
++	/* For TCP portals, kill the TCP socket every time. */
++	if (esp->esp_dest.proto == IPPROTO_TCP)
++		isns_esi_disconnect(esp);
++
++	if (esp->esp_socket == NULL) {
++		sock = isns_connect_to_portal(&esp->esp_dest);
++		if (sock == NULL)
++			return;
++
++		isns_socket_set_security_ctx(sock,
++			isns_default_security_context(0));
++		/* sock->is_disconnect_fatal = 1; */
++		esp->esp_socket = sock;
++	}
++
++	isns_attr_list_append_uint64(&attrs,
++				ISNS_TAG_TIMESTAMP,
++				time(NULL));
++	/* The following will extract the ENTITY IDENTIFIER */
++	isns_object_extract_keys(esi->esi_object, &attrs);
++	isns_portal_to_attr_list(&esp->esp_portal,
++				ISNS_TAG_PORTAL_IP_ADDRESS,
++				ISNS_TAG_PORTAL_TCP_UDP_PORT,
++				&attrs);
++
++	msg = isns_simple_create(ISNS_ENTITY_STATUS_INQUIRY,
++					NULL, &attrs);
++	if (msg == NULL)
++		return;
++
++	isns_debug_esi("*** Sending ESI message to %s (xid=0x%x); %u retries left\n",
++			isns_portal_string(&esp->esp_dest),
++			msg->is_xid, esp->esp_retries);
++	isns_simple_transmit(esp->esp_socket, msg,
++			NULL, esp->esp_timeout - 1,
++			isns_process_esi_response);
++	esp->esp_xid = msg->is_xid;
++	isns_simple_free(msg);
++}
++
++/*
++ * A new entity was added. See if it uses ESI, and create
++ * portals and such.
++ */
++static void
++isns_esi_add_entity(isns_object_t *obj)
++{
++	isns_esi_t	*esi;
++
++	isns_debug_esi("Enable ESI monitoring for entity %u\n", obj->ie_index);
++	esi = isns_calloc(1, sizeof(*esi));
++	esi->esi_object = isns_object_get(obj);
++	esi->esi_update = 1;
++	isns_list_init(&esi->esi_list);
++	isns_list_init(&esi->esi_portals);
++
++	isns_list_append(&isns_esi_list, &esi->esi_list);
++}
++
++/*
++ * Given an entity, see if we can find ESI state for it.
++ */
++static isns_esi_t *
++isns_esi_find(isns_object_t *obj)
++{
++	isns_list_t	*pos, *next;
++
++	isns_list_foreach(&isns_esi_list, pos, next) {
++		isns_esi_t	*esi = isns_list_item(isns_esi_t, esi_list, pos);
++
++		if (esi->esi_object == obj)
++			return esi;
++	}
++	return NULL;
++}
++
++/*
++ * Update the ESI state after an entity has changed
++ */
++static int
++isns_esi_update(isns_esi_t *esi)
++{
++	isns_object_t	*entity = esi->esi_object;
++	ISNS_LIST_DECLARE(hold);
++	isns_esi_portal_t *esp;
++	unsigned int	i;
++
++	isns_debug_esi("Updating ESI state for entity %u\n", entity->ie_index);
++
++	isns_list_move(&hold, &esi->esi_portals);
++	for (i = 0; i < entity->ie_children.iol_count; ++i) {
++		isns_object_t	*child = entity->ie_children.iol_data[i];
++		isns_portal_info_t esi_portal, portal_info;
++		uint32_t	esi_interval;
++		isns_list_t	*pos, *next;
++		int		changed = 0;
++
++		if (!ISNS_IS_PORTAL(child))
++			continue;
++
++		if (!isns_portal_from_object(&portal_info,
++					ISNS_TAG_PORTAL_IP_ADDRESS,
++					ISNS_TAG_PORTAL_TCP_UDP_PORT,
++					child)
++		 || !isns_portal_from_object(&esi_portal,
++					ISNS_TAG_PORTAL_IP_ADDRESS,
++					ISNS_TAG_ESI_PORT,
++					child)
++		 || !isns_object_get_uint32(child,
++					ISNS_TAG_ESI_INTERVAL,
++					&esi_interval))
++			continue;
++
++		isns_list_foreach(&hold, pos, next) {
++			esp = isns_list_item(isns_esi_portal_t, esp_list, pos);
++
++			if (esp->esp_object == child) {
++				isns_debug_esi("Updating ESI state for %s\n",
++						isns_portal_string(&portal_info));
++				isns_list_del(&esp->esp_list);
++				goto update;
++			}
++		}
++
++		isns_debug_esi("Creating ESI state for %s\n",
++				isns_portal_string(&portal_info));
++		esp = isns_calloc(1, sizeof(*esp));
++		esp->esp_object = isns_object_get(child);
++		isns_list_init(&esp->esp_list);
++		changed = 1;
++
++update:
++		if (!isns_portal_equal(&esp->esp_portal, &portal_info)) {
++			esp->esp_portal = portal_info;
++			changed++;
++		}
++		if (!isns_portal_equal(&esp->esp_dest, &esi_portal)) {
++			isns_esi_disconnect(esp);
++			esp->esp_dest = esi_portal;
++			changed++;
++		}
++		if (esp->esp_interval != esi_interval) {
++			esp->esp_interval = esi_interval;
++			changed++;
++		}
++
++		isns_esi_restart(esp);
++
++		isns_list_append(&esi->esi_portals, &esp->esp_list);
++	}
++
++	/* Destroy any old ESI portals */
++	while (!isns_list_empty(&hold)) {
++		esp = isns_list_item(isns_esi_portal_t, esp_list, hold.next);
++
++		isns_esi_drop_portal(esp, NULL, 0);
++	}
++
++	/* If the client explicitly unregistered all ESI portals,
++	 * stop monitoring it but *without* destroying the entity. */
++	if (isns_list_empty(&esi->esi_portals)) {
++		isns_esi_drop_entity(esi, NULL, 0);
++		return 0;
++	}
++
++	return 1;
++}
++
++void
++isns_esi_restart(isns_esi_portal_t *esp)
++{
++	unsigned int	timeo;
++
++	isns_esi_disconnect(esp);
++
++	esp->esp_start = time(NULL);
++	esp->esp_retries = isns_config.ic_esi_retries;
++	esp->esp_next_xmit = esp->esp_start + esp->esp_interval;
++	esp->esp_xid = 0;
++
++	timeo = esp->esp_interval / esp->esp_retries;
++	if (timeo == 0)
++		timeo = 1;
++	else if (timeo > ESI_RETRANS_TIMEOUT)
++		timeo = ESI_RETRANS_TIMEOUT;
++	esp->esp_timeout = timeo;
++}
++
++void
++isns_esi_disconnect(isns_esi_portal_t *esp)
++{
++	if (esp->esp_socket)
++		isns_socket_free(esp->esp_socket);
++	esp->esp_socket = NULL;
++}
++
++/*
++ * Generic wrapper to dropping an object
++ */
++static inline void
++__isns_esi_drop_object(isns_db_t *db, isns_object_t *obj, unsigned int dead)
++{
++	if (db && obj && obj->ie_state == ISNS_OBJECT_STATE_MATURE && dead)
++		isns_db_remove(db, obj);
++	isns_object_release(obj);
++}
++
++/*
++ * Portal did not respond in time. Drop it
++ */
++void
++isns_esi_drop_portal(isns_esi_portal_t *esp, isns_db_t *db, int dead)
++{
++	isns_debug_esi("ESI: dropping portal %s\n",
++			isns_portal_string(&esp->esp_portal));
++
++	isns_list_del(&esp->esp_list);
++	isns_esi_disconnect(esp);
++	__isns_esi_drop_object(db, esp->esp_object, dead);
++	isns_free(esp);
++}
++
++/*
++ * We ran out of ESI portals for this entity.
++ */
++void
++isns_esi_drop_entity(isns_esi_t *esi, isns_db_t *db, int dead)
++{
++	isns_debug_esi("ESI: dropping entity %u\n",
++			esi->esi_object->ie_index);
++
++	isns_list_del(&esi->esi_list);
++	__isns_esi_drop_object(db, esi->esi_object, dead);
++
++	while (!isns_list_empty(&esi->esi_portals)) {
++		isns_esi_portal_t *esp;
++
++		esp = isns_list_item(isns_esi_portal_t, esp_list,
++				esi->esi_portals.next);
++		isns_esi_drop_portal(esp, db, dead);
++	}
++	isns_free(esi);
++}
++
++/*
++ * When receiving an ESI response, find the portal we sent the
++ * original message to.
++ */
++static isns_esi_portal_t *
++isns_esi_get_msg_portal(uint32_t xid, isns_esi_t **esip)
++{
++	isns_list_t	*esi_pos, *esi_next;
++
++	isns_list_foreach(&isns_esi_list, esi_pos, esi_next) {
++		isns_esi_t	*esi = isns_list_item(isns_esi_t, esi_list, esi_pos);
++		isns_list_t	*esp_pos, *esp_next;
++
++		isns_list_foreach(&esi->esi_portals, esp_pos, esp_next) {
++			isns_esi_portal_t *esp = isns_list_item(isns_esi_portal_t,
++							esp_list, esp_pos);
++
++			if (esp->esp_xid == xid) {
++				*esip = esi;
++				return esp;
++			}
++		}
++	}
++
++	return NULL;
++}
++
++/*
++ * Handle incoming ESI request
++ */
++int
++isns_process_esi(isns_server_t *srv, isns_simple_t *call, isns_simple_t **reply)
++{
++	const isns_attr_list_t *attrs = &call->is_message_attrs;
++	isns_object_t	*portal = NULL;
++
++	/* We just echo back the attributes sent to us by the server,
++	 * without further checking. */
++	*reply = isns_simple_create(ISNS_ENTITY_STATUS_INQUIRY,
++				srv->is_source, attrs);
++
++	/* Look up the portal and update its mtime.
++	 * This can help the application find out if a portal has
++	 * seen ESIs recently, and react.
++	 */
++	if (srv->is_db && attrs->ial_count == 4) {
++		const isns_attr_t	*addr_attr, *port_attr;
++
++		addr_attr = attrs->ial_data[2];
++		port_attr = attrs->ial_data[3];
++		if (addr_attr->ia_tag_id == ISNS_TAG_PORTAL_IP_ADDRESS
++		 && port_attr->ia_tag_id == ISNS_TAG_PORTAL_TCP_UDP_PORT) {
++			isns_attr_list_t key;
++
++			key.ial_count = 2;
++			key.ial_data = attrs->ial_data + 2;
++			portal = isns_db_lookup(srv->is_db,
++					&isns_portal_template,
++					&key);
++		}
++
++		if (portal)
++			portal->ie_mtime = time(NULL);
++	}
++	return ISNS_SUCCESS;
++}
++
++void
++isns_process_esi_response(uint32_t xid, int status, isns_simple_t *msg)
++{
++	isns_portal_info_t	portal_info;
++	isns_esi_portal_t	*esp;
++	isns_esi_t		*esi;
++
++	if (msg == NULL) {
++		isns_debug_esi("ESI call 0x%x timed out\n", xid);
++		return;
++	}
++
++	/* FIXME: As a matter of security, we should probably
++	 * verify that the ESI response originated from the
++	 * portal we sent it to; or at least that it was authenticated
++	 * by the client we think we're talking to. */
++
++	/* Get the portal */
++	if (!isns_portal_from_attr_list(&portal_info,
++				ISNS_TAG_PORTAL_IP_ADDRESS,
++				ISNS_TAG_PORTAL_TCP_UDP_PORT,
++				&msg->is_message_attrs)) {
++		isns_debug_esi("Ignoring unintelligible ESI response\n");
++		return;
++	}
++
++	if (!(esp = isns_esi_get_msg_portal(xid, &esi))) {
++		isns_debug_esi("Ignoring unmatched ESI reply\n");
++		return;
++	}
++
++	if (!isns_portal_equal(&esp->esp_portal, &portal_info)) {
++		isns_warning("Faked ESI response for portal %s\n",
++				isns_portal_string(&portal_info));
++		return;
++	}
++
++	isns_debug_esi("Good ESI response from %s\n",
++				isns_portal_string(&portal_info));
++	isns_esi_restart(esp);
++
++	/* Refresh the entity's registration timestamp */
++	isns_object_set_uint64(esi->esi_object,
++			ISNS_TAG_TIMESTAMP,
++			time(NULL));
++	isns_db_sync(isns_esi_server->is_db);
++}
++
++/*
++ * Helper function to schedule the next timeout
++ */
++static void
++isns_esi_schedule(int timeout)
++{
++	isns_cancel_timer(isns_esi_transmit, NULL);
++	isns_add_oneshot_timer(timeout, isns_esi_transmit, NULL);
++}
++
++/*
++ * Register an entity for ESI monitoring.
++ * This is called when reloading the database.
++ */
++void
++isns_esi_register(isns_object_t *obj)
++{
++	if (!isns_esi_find(obj))
++		isns_esi_add_entity(obj);
++	/* We do not call esi_schedule(0) here; that happens in
++	 * isns_esi_init already. */
++}
++
++/*
++ * This callback is invoked whenever an object is added/removed/modified.
++ * We use this to keep track of ESI portals and such.
++ */
++void
++isns_esi_callback(const isns_db_event_t *ev, void *ptr)
++{
++	isns_object_t	*obj, *entity;
++	isns_esi_t	*esi;
++	uint32_t	event;
++
++	obj = ev->ie_object;
++	event = ev->ie_bits;
++
++	if (obj->ie_flags & ISNS_OBJECT_PRIVATE)
++		return;
++
++	isns_debug_esi("isns_esi_callback(%p, 0x%x)\n", obj, event);
++
++	if (ISNS_IS_ENTITY(obj)
++	 && (event & ISNS_SCN_OBJECT_ADDED_MASK)) {
++		if (!isns_esi_find(obj))
++			isns_esi_add_entity(obj);
++		/* Schedule an immediate ESI timer run */
++		isns_esi_schedule(0);
++		return;
++	}
++
++	if (!(entity = isns_object_get_entity(obj)))
++		return;
++
++	esi = isns_esi_find(entity);
++	if (esi != NULL)
++		esi->esi_update = 1;
++
++	/* Schedule an immediate ESI timer run */
++	isns_esi_schedule(0);
++}
+diff --git a/utils/open-isns/etc/isnsadm.conf b/utils/open-isns/etc/isnsadm.conf
+new file mode 100644
+index 0000000..e7ee681
+--- /dev/null
++++ b/utils/open-isns/etc/isnsadm.conf
+@@ -0,0 +1,73 @@
++#
++# Sample iSNS client configuration file
++#
++
++# The source name. This is an iSCSI qualified name,
++# and identifies the client uniquely.
++#
++# If left empty, the source name is derived from
++# the client's hostname.
++#
++#SourceName	= iqn.2006-01.com.example.host1
++
++# Name and port of the iSNS server.
++# Possible formats:
++#	foo.example.com
++#	foo.example.com:3205
++#	192.168.1.7:isns
++#	[2001:4e5f::1]:isns
++#	SLP:
++# If the special string "SLP:" is given, Open-iSNS will
++# query the SLP directory service to find the iSNS server.
++#ServerAddress	= isns.example.com
++
++
++# Authentication enable/disable.
++# When set to 1, the client will sign
++# all messages, and expect all server messages
++# to be signed.
++#
++# Authentication requires a valid private DSA
++# key in AuthKeyFile, and the server's DSA public
++# key in ServerKeyFile.
++#
++# The default is to use authentication if the
++# requires keys are installed, and use unauthenticated
++# iSNS otherwise.
++#Security	= 1
++
++# Location of the client's private key.
++# The file must contain a PEM encoded DSA key.
++# The default is /etc/isns/auth_key
++#AuthKeyFile	= /etc/isns/auth_key
++
++# Location of the servers's public key.
++# The file must contain a PEM encoded DSA key.
++# The default is /etc/isns/server_key.pub
++#ServerKeyFile	= /etc/isns/server_key.pub
++
++# In order to prevent replay attacks, the
++# authentication blocks carried by iSNS
++# include a time stamp. The following two
++# parameters control how we verify the
++# time stamp
++Auth.ReplayWindow = 2m
++Auth.TimeStampJitter = 1s
++
++# Maximum number of incoming connections
++# accepted. This usually applies to server
++# side only, but is relevant if you create
++# a passive TCP socket for ESI or SCN.
++# Network.MaxSockets = 1024
++
++# Time to wait for a TCP connection to be
++# established.
++# Network.ConnectTimeout = 60
++
++# When a connection attempt failed, we wait
++# before we try connecting again.
++# Network.ReonnectTimeout = 10
++
++# Total amount of time to wait before timing
++# out a call to the iSNS server.
++# Network.CallTimeout = 60
+diff --git a/utils/open-isns/etc/isnsd.conf b/utils/open-isns/etc/isnsd.conf
+new file mode 100644
+index 0000000..bc90f40
+--- /dev/null
++++ b/utils/open-isns/etc/isnsd.conf
+@@ -0,0 +1,129 @@
++#
++# Sample iSNS Server configuration file
++#
++
++# The source name. This is an iSCSI qualified name,
++# and identifies the client uniquely.
++#
++# If left empty, the source name is derived from
++# the client's hostname.
++#
++#SourceName	= iqn.2006-01.com.example.host1
++
++# Where to store the database.
++# If you leave this empty, isnsd will keep its
++# database in memory.
++# Setting this to an absolute path name will
++# make isnsd keep its database in a directory
++# hierarchy below that directory.
++Database	= /var/lib/isns
++
++# The iSNS server can purge registered entities
++# after a certain period of inactivity. This is
++# called the registration period.
++# Clients who register objects are supposed to
++# refresh their registration within this period.
++#
++# The default value is 0, which disables this
++# feature.
++RegistrationPeriod	= 10m
++
++# iSNS scopes visibility of other nodes using so-called
++# Discovery Domains. A storage node A will only "see"
++# storage node B, if both are members of the same
++# discovery domain.
++#
++# So if a storage node is registered which is not part of
++# any discovery domain, it will not see any other nodes.
++#
++# By setting DefaultDiscoveryDomain=1, you can tell isnsd to
++# create a virtual "default discovery domain", which 
++# holds all nodes that are not part of any administratively
++# configured discovery domain.
++DefaultDiscoveryDomain	= 1
++
++# Make the iSNS server register itself with SLP.
++# Clients will be able to discover the server by
++# querying for service type "iscsi:sms", and a query
++# of "(protocols=isns)"
++SLPRegister		= 1
++
++# Authentication enable/disable.
++# When set to 1, the client will sign
++# all messages, and expect all server messages
++# to be signed.
++#
++# Authentication requires a valid private DSA
++# key in AuthKeyFile, and the server's DSA public
++# key in ServerKeyFile.
++#
++# The default is to use authentication if the
++# requires keys are installed, and use unauthenticated
++# iSNS otherwise.
++#Security	= 1
++
++# Location of the client's private key.
++# The file must contain a PEM encoded DSA key.
++# The default is /etc/isns/auth_key
++#AuthKeyFile	= /etc/isns/auth_key
++
++# Location of the servers's public key.
++# The file must contain a PEM encoded DSA key.
++# The default is /etc/isns/server_key.pub
++#ServerKeyFile	= /etc/isns/server_key.pub
++
++# This describes where the iSNS server stores
++# authentication keys and policy information.
++# Two options are currently supported: a
++# simple key store (flat directory with public
++# keys in PEM encoded files), and the iSNS
++# database itself
++#ClientKeyStore	= /etc/isns/keystores
++ClientKeyStore	= DB:
++
++# When transmitting State Change Notification,
++# we expect the client to ack them. If the
++# ACK doesn't arrive in due time, we retransmit
++# for a limited number of attempts, cycling
++# through the available portals.
++SCNTimeout	= 60
++SCNRetries	= 3
++
++# Configuration of ESI.
++# Defaults are
++#  ESIMaxInterval = 1h
++#  ESIMinInterval = 60s
++#  ESIRetries     = 3
++# Setting ESIRetries to 0 disables ESI support, and makes
++# the server reject any portal registrations that specify
++# an ESI portal.
++ESIMinInterval	= 1m
++ESIMaxInterval	= 2m
++ESIRetries	= 3
++
++# In order to prevent replay attacks, the
++# authentication blocks carried by iSNS
++# include a time stamp. The following two
++# parameters control how we verify the
++# time stamp
++Auth.ReplayWindow = 2m
++Auth.TimeStampJitter = 1s
++
++# Maximum number of incoming connections
++# accepted.
++# Network.MaxSockets = 1024
++
++# Time to wait for a TCP connection to be
++# established.
++# (Client only)
++# Network.ConnectTimeout = 60
++
++# When a connection attempt failed, we wait
++# before we try connecting again.
++# (Client only)
++# Network.ReonnectTimeout = 10
++
++# Total amount of time to wait before timing
++# out a call to the iSNS server.
++# (Client only)
++# Network.CallTimeout = 60
+diff --git a/utils/open-isns/etc/isnsdd.conf b/utils/open-isns/etc/isnsdd.conf
+new file mode 100644
+index 0000000..d751c3d
+--- /dev/null
++++ b/utils/open-isns/etc/isnsdd.conf
+@@ -0,0 +1,72 @@
++#
++# Sample iSNS Discovery Daemon configuration file
++#
++
++# The source name. This is an iSCSI qualified name,
++# and identifies the client uniquely.
++#
++# If left empty, the source name is derived from
++# the client's hostname.
++#
++#SourceName	= iqn.2006-01.com.example.host1:monitor
++
++# Name and port of the iSNS server.
++# Possible formats:
++#	foo.example.com
++#	foo.example.com:3205
++#	192.168.1.7:isns
++#	[2001:4e5f::1]:isns
++#	SLP:
++# If the special string "SLP:" is given, Open-iSNS will
++# query the SLP directory service to find the iSNS server.
++#ServerAddress	= isns.example.com
++
++# Authentication enable/disable.
++# When set to 1, the client will sign
++# all messages, and expect all server messages
++# to be signed.
++#
++# Authentication requires a valid private DSA
++# key in AuthKeyFile, and the server's DSA public
++# key in ServerKeyFile.
++#
++# The default is to use authentication if the
++# required keys are installed, and use unauthenticated
++# iSNS otherwise.
++#Security	= 1
++
++# Location of the client's private key.
++# The file must contain a PEM encoded DSA key.
++# The default is /etc/isns/auth_key
++#AuthKeyFile	= /etc/isns/auth_key
++
++# Location of the servers's public key.
++# The file must contain a PEM encoded DSA key.
++# The default is /etc/isns/server_key.pub
++#ServerKeyFile	= /etc/isns/server_key.pub
++
++# In order to prevent replay attacks, the
++# authentication blocks carried by iSNS
++# include a time stamp. The following two
++# parameters control how we verify the
++# time stamp
++Auth.ReplayWindow = 2m
++Auth.TimeStampJitter = 1s
++
++# Maximum number of incoming connections
++# accepted. This usually applies to server
++# side only, but is relevant if you create
++# a passive TCP socket for ESI or SCN.
++# Network.MaxSockets = 1024
++
++# Time to wait for a TCP connection to be
++# established.
++# Network.ConnectTimeout = 60
++
++# When a connection attempt failed, we wait
++# before we try connecting again.
++# Network.ReonnectTimeout = 10
++
++# Total amount of time to wait before timing
++# out a call to the iSNS server.
++# Network.CallTimeout = 60
+diff --git a/utils/open-isns/etc/openisns.init b/utils/open-isns/etc/openisns.init
+new file mode 100644
+index 0000000..7c03778
+--- /dev/null
++++ b/utils/open-isns/etc/openisns.init
+@@ -0,0 +1,71 @@
++#!/bin/sh
++#
++# Init script for Open-iSNS.
++#
++# Copyright (C) 2007 Albert Pauw
++#
++# chkconfig: 345 13 89
++# description: Starts and stops the iSCSI isns server
++#
++# processname: isnsd
++# pidfile: /var/run/isnsd.pid
++# config:  /etc/isns/isnsd.conf
++
++# Source function library.
++. /etc/init.d/functions
++
++PATH=/sbin:/bin:/usr/sbin:/usr/bin
++#OPTIONS="-4 -d all"
++CONFIG="-c /etc/isns/isnsd.conf"
++RETVAL=0
++
++start()
++{
++         echo -n "Starting iSCSI isns service: "
++         daemon isnsd $OPTIONS $CONFIG
++         RETVAL=$?
++         success
++         echo
++         [ $RETVAL -eq 0 ] || return
++         touch /var/lock/subsys/open-isns
++}
++
++stop()
++{
++         echo -n "Stopping iSCSI isns service: "
++         killproc isnsd
++         [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/open-isns
++         success
++         echo
++
++}
++
++restart()
++{
++         stop
++         start
++}
++
++case "$1" in
++start)
++	start
++	;;
++stop)
++	stop
++	;;
++restart)
++	restart
++	;;
++status)
++	status isnsd
++	RETVAL=$?
++	;;
++condrestart)
++	[ -f /var/lock/subsys/open-isns ] && restart
++	;;
++*)
++	echo $"Usage: $0 {start|stop|restart|status|condrestart}"
++	exit 1
++esac
++
++exit $RETVAL
+diff --git a/utils/open-isns/export.c b/utils/open-isns/export.c
+new file mode 100644
+index 0000000..fa4c278
+--- /dev/null
++++ b/utils/open-isns/export.c
+@@ -0,0 +1,547 @@
++/*
++ * Helper functions to represent iSNS objects as text,
++ * and/or to parse objects represented in textual form.
++ * These functions can be used by command line utilities
++ * such as isnsadm, as well as applications like iscsid
++ * or stgtd when talking to the iSNS discovery daemon.
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <ctype.h>
++
++#include "isns.h"
++#include "util.h"
++#include "vendor.h"
++#include "attrs.h"
++#include "security.h"
++#include "objects.h"
++#include "paths.h"
++
++#define MAX_ALIASES		4
++
++struct isns_tag_prefix {
++	const char *		name;
++	unsigned int		name_len;
++	isns_object_template_t *context;
++};
++
++struct tag_name {
++	const char *		name;
++	uint32_t		tag;
++	struct isns_tag_prefix *prefix;
++	const char *		alias[MAX_ALIASES];
++};
++
++static struct isns_tag_prefix all_prefixes[__ISNS_OBJECT_TYPE_MAX] = {
++[ISNS_OBJECT_TYPE_ENTITY] = { "entity-", 7, &isns_entity_template	},
++[ISNS_OBJECT_TYPE_NODE]   = { "iscsi-",	 6, &isns_iscsi_node_template	},
++[ISNS_OBJECT_TYPE_PORTAL] = { "portal-", 7, &isns_portal_template	},
++[ISNS_OBJECT_TYPE_PG]     = { "pg-",	 3, &isns_iscsi_pg_template	},
++[ISNS_OBJECT_TYPE_DD]     = { "dd-",	 3, &isns_dd_template		},
++[ISNS_OBJECT_TYPE_POLICY] = { "policy-", 7, &isns_policy_template	},
++};
++
++static struct tag_name	all_attrs[] = {
++{ "id",			ISNS_TAG_ENTITY_IDENTIFIER,
++			.alias = { "eid", },
++},
++{ "prot",		ISNS_TAG_ENTITY_PROTOCOL },
++{ "idx",		ISNS_TAG_ENTITY_INDEX },
++
++{ "name",		ISNS_TAG_ISCSI_NAME },
++{ "node-type",		ISNS_TAG_ISCSI_NODE_TYPE },
++{ "alias",		ISNS_TAG_ISCSI_ALIAS },
++{ "authmethod",		ISNS_TAG_ISCSI_AUTHMETHOD },
++{ "idx",		ISNS_TAG_ISCSI_NODE_INDEX },
++
++{ "addr",		ISNS_TAG_PORTAL_IP_ADDRESS },
++{ "port",		ISNS_TAG_PORTAL_TCP_UDP_PORT },
++{ "name",		ISNS_TAG_PORTAL_SYMBOLIC_NAME },
++{ "esi-port",		ISNS_TAG_ESI_PORT },
++{ "esi-interval",	ISNS_TAG_ESI_INTERVAL },
++{ "scn-port",		ISNS_TAG_SCN_PORT },
++{ "idx",		ISNS_TAG_PORTAL_INDEX },
++
++{ "name",		ISNS_TAG_PG_ISCSI_NAME },
++{ "addr",		ISNS_TAG_PG_PORTAL_IP_ADDR },
++{ "port",		ISNS_TAG_PG_PORTAL_TCP_UDP_PORT },
++{ "tag",		ISNS_TAG_PG_TAG },
++{ "pgt",		ISNS_TAG_PG_TAG },
++{ "idx",		ISNS_TAG_PG_INDEX },
++
++{ "id",			ISNS_TAG_DD_ID },
++{ "name",		ISNS_TAG_DD_SYMBOLIC_NAME },
++{ "member-name",	ISNS_TAG_DD_MEMBER_ISCSI_NAME },
++{ "member-iscsi-idx",	ISNS_TAG_DD_MEMBER_ISCSI_INDEX },
++{ "member-fc-name",	ISNS_TAG_DD_MEMBER_FC_PORT_NAME },
++{ "member-portal-idx",	ISNS_TAG_DD_MEMBER_PORTAL_INDEX },
++{ "member-addr",	ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR  },
++{ "member-port",	ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT  },
++{ "features",		ISNS_TAG_DD_FEATURES },
++
++{ "name",		OPENISNS_TAG_POLICY_SPI,
++			.alias = { "spi" },
++},
++{ "key",		OPENISNS_TAG_POLICY_KEY },
++{ "entity",		OPENISNS_TAG_POLICY_ENTITY },
++{ "object-type",	OPENISNS_TAG_POLICY_OBJECT_TYPE },
++{ "node-type",		OPENISNS_TAG_POLICY_NODE_TYPE },
++{ "node-name",		OPENISNS_TAG_POLICY_NODE_NAME },
++{ "functions",		OPENISNS_TAG_POLICY_FUNCTIONS },
++
++{ NULL }
++};
++
++/*
++ * Initialize tag array
++ */
++static void
++init_tags(void)
++{
++	struct tag_name	*t;
++
++	for (t = all_attrs; t->name; ++t) {
++		isns_object_template_t *tmpl;
++
++		tmpl = isns_object_template_for_tag(t->tag);
++		if (tmpl == NULL)
++			isns_fatal("Bug: cannot find object type for tag %s\n",
++					t->name);
++		t->prefix = &all_prefixes[tmpl->iot_handle];
++	}
++}
++
++/*
++ * Match prefix
++ */
++static struct isns_tag_prefix *
++find_prefix(const char *name)
++{
++	struct isns_tag_prefix *p;
++	unsigned int	i;
++
++	for (i = 0, p = all_prefixes; i < __ISNS_OBJECT_TYPE_MAX; ++i, ++p) {
++		if (p->name && !strncmp(name, p->name, p->name_len))
++			return p;
++	}
++	return NULL;
++}
++
++/*
++ * Look up the tag for a given attribute name.
++ * By default, attr names come with a disambiguating
++ * prefix that defines the object type the attribute applies
++ * to, such as "entity-" or "portal-". Once a context has
++ * been established (ie we know the object type subsequent
++ * attributes apply to), specifying the prefix is optional.
++ *
++ * For instance, in a portal context, "addr=10.1.1.1 port=616 name=foo"
++ * specifies three portal related attributes. Whereas in a portal
++ * group context, the same string would specify three portal group
++ * related attributes. To disambiguate, the first attribute in
++ * this list should be prefixed by "portal-" or "pg-", respectively.
++ */
++static uint32_t
++tag_by_name(const char *name, struct isns_attr_list_parser *st)
++{
++	const char	*orig_name = name;
++	unsigned int	nmatch = 0, i;
++	struct tag_name	*t, *match[8];
++	struct isns_tag_prefix *specific = NULL;
++
++	if (all_attrs[0].prefix == NULL)
++		init_tags();
++
++	specific = find_prefix(name);
++	if (specific != NULL) {
++		if (st->prefix
++		 && st->prefix != specific
++		 && !st->multi_type_permitted) {
++			isns_error("Cannot mix attributes of different types\n");
++			return 0;
++		}
++		name += specific->name_len;
++		st->prefix = specific;
++	}
++
++	for (t = all_attrs; t->name; ++t) {
++		if (specific && t->prefix != specific)
++			continue;
++		if (!st->multi_type_permitted
++		 && st->prefix && t->prefix != st->prefix)
++			continue;
++		if (!strcmp(name, t->name))
++			goto match;
++		for (i = 0; i < MAX_ALIASES && t->alias[i]; ++i) {
++			if (!strcmp(name, t->alias[i]))
++				goto match;
++		}
++		continue;
++
++match:
++		if (nmatch < 8)
++			match[nmatch++] = t;
++	}
++
++	if (nmatch > 1) {
++		char		conflict[128];
++		unsigned int	i;
++
++		conflict[0] = '\0';
++		for (i = 0; i < nmatch; ++i) {
++			if (i)
++				strcat(conflict, ", ");
++			t = match[i];
++			strcat(conflict, t->prefix->name);
++			strcat(conflict, t->name);
++		}
++		isns_error("tag name \"%s\" not unique in this context "
++				"(could be one of %s)\n",
++				orig_name, conflict);
++		return 0;
++	}
++
++	if (nmatch == 0) {
++		isns_error("tag name \"%s\" not known in this context\n",
++				orig_name);
++		return 0;
++	}
++
++	st->prefix = match[0]->prefix;
++	return match[0]->tag;
++}
++
++static const char *
++name_by_tag(uint32_t tag, struct isns_attr_list_parser *st)
++{
++	struct tag_name *t;
++
++	for (t = all_attrs; t->name; ++t) {
++		if (st->prefix && t->prefix != st->prefix)
++			continue;
++		if (t->tag == tag)
++			return t->name;
++	}
++	return NULL;
++}
++
++static int
++parse_one_attr(const char *name, const char *value,
++		isns_attr_list_t *attrs,
++		struct isns_attr_list_parser *st)
++{
++	isns_attr_t	*attr;
++	uint32_t	tag;
++
++	/* Special case: "portal=<address:port>" is translated to
++	 * addr=<address> port=<port>
++	 * If no context has been set, assume portal context.
++	 */
++	if (!strcasecmp(name, "portal")) {
++		isns_portal_info_t portal_info;
++		uint32_t	addr_tag, port_tag;
++
++		if (st->prefix == NULL) {
++			addr_tag = tag_by_name("portal-addr", st);
++			port_tag = tag_by_name("portal-port", st);
++		} else {
++			addr_tag = tag_by_name("addr", st);
++			port_tag = tag_by_name("port", st);
++		}
++
++		if (!addr_tag || !port_tag) {
++			isns_error("portal=... not supported in this context\n");
++			return 0;
++		}
++		if (value == NULL) {
++			isns_attr_list_append_nil(attrs, addr_tag);
++			isns_attr_list_append_nil(attrs, port_tag);
++			return 1;
++		}
++		if (!isns_portal_parse(&portal_info, value, st->default_port))
++			return 0;
++		isns_portal_to_attr_list(&portal_info, addr_tag, port_tag, attrs);
++		return 1;
++	}
++
++	if (!(tag = tag_by_name(name, st)))
++		return 0;
++
++	/* Special handling for key objects */
++	if (tag == OPENISNS_TAG_POLICY_KEY) {
++		if (!value || !strcasecmp(value, "gen")) {
++			if (st->generate_key == NULL) {
++				isns_error("Key generation not supported in this context\n");
++				return 0;
++			}
++			attr = st->generate_key();
++		} else {
++			if (st->load_key == NULL) {
++				isns_error("Policy-key attribute not supported in this context\n");
++				return 0;
++			}
++			attr = st->load_key(value);
++		}
++		goto append_attr;
++	}
++
++	if (value == NULL) {
++		isns_attr_list_append_nil(attrs, tag);
++		return 1;
++	}
++
++	attr = isns_attr_from_string(tag, value);
++	if (!attr)
++		return 0;
++
++append_attr:
++	isns_attr_list_append_attr(attrs, attr);
++	return 1;
++}
++
++void
++isns_attr_list_parser_init(struct isns_attr_list_parser *st,
++				isns_object_template_t *tmpl)
++{
++	if (all_attrs[0].prefix == NULL)
++		init_tags();
++
++	memset(st, 0, sizeof(*st));
++	if (tmpl)
++		st->prefix = &all_prefixes[tmpl->iot_handle];
++}
++
++int
++isns_attr_list_split(char *line, char **argv, unsigned int argc_max)
++{
++	char		*src = line;
++	unsigned int	argc = 0, quoted = 0;
++
++	if (!line)
++		return 0;
++
++	while (1) {
++		char	*dst;
++
++		while (isspace(*src))
++			++src;
++		if (!*src)
++			break;
++
++		argv[argc] = dst = src;
++		while (*src) {
++			char cc = *src++;
++
++			if (cc == '"') {
++				quoted = !quoted;
++				continue;
++			}
++			if (!quoted && isspace(cc)) {
++				*dst = '\0';
++				break;
++			}
++			*dst++ = cc;
++		}
++
++		if (quoted) {
++			isns_error("%s: Unterminated quoted string: \"%s\"\n",
++					__FUNCTION__, argv[argc]);
++			return -1;
++		}
++		argc++;
++	}
++
++	return argc;
++}
++
++int
++isns_parse_attrs(unsigned int argc, char **argv,
++		isns_attr_list_t *attrs,
++		struct isns_attr_list_parser *st)
++{
++	unsigned int	i;
++
++	for (i = 0; i < argc; ++i) {
++		char		*name, *value;
++
++		name = argv[i];
++		if ((value = strchr(name, '=')) != NULL)
++			*value++ = '\0';
++
++		if (!value && !st->nil_permitted) {
++			isns_error("Missing value for atribute %s\n", name);
++			return 0;
++		}
++
++		if (!parse_one_attr(name, value, attrs, st)) {
++			isns_error("Unable to parse %s=%s\n", name, value);
++			return 0;
++		}
++	}
++
++	return 1;
++}
++
++/*
++ * Query strings may contain a mix of query keys (foo=bar),
++ * and requested attributes (?foo). The former are used by
++ * the server in its object search, whereas the latter instruct
++ * it which attributes to return.
++ */
++int
++isns_parse_query_attrs(unsigned int argc, char **argv,
++		isns_attr_list_t *keys,
++		isns_attr_list_t *requested_attrs,
++		struct isns_attr_list_parser *st)
++{
++	struct isns_attr_list_parser query_state;
++	unsigned int	i;
++
++	query_state = *st;
++	query_state.multi_type_permitted = 1;
++
++	for (i = 0; i < argc; ++i) {
++		char		*name, *value;
++
++		name = argv[i];
++		if ((value = strchr(name, '=')) != NULL)
++			*value++ = '\0';
++
++		if (name[0] == '?') {
++			uint32_t tag;
++
++			if (value) {
++				isns_error("No value allowed for query attribute %s\n",
++						name);
++				return 0;
++			}
++
++			if ((tag = tag_by_name(name + 1, &query_state)) != 0) {
++				isns_attr_list_append_nil(requested_attrs, tag);
++				continue;
++			}
++		} else {
++			if (!value && !st->nil_permitted) {
++				isns_error("Missing value for atribute %s\n", name);
++				return 0;
++			}
++
++			if (parse_one_attr(name, value, keys, st))
++				continue;
++		}
++
++		isns_error("Unable to parse %s=%s\n", name, value);
++		return 0;
++	}
++
++	return 1;
++}
++
++void
++isns_attr_list_parser_help(struct isns_attr_list_parser *st)
++{
++	isns_object_template_t *tmpl, *current = NULL;
++	struct tag_name	*t;
++
++	if (all_attrs[0].prefix == NULL)
++		init_tags();
++
++	for (t = all_attrs; t->name; ++t) {
++		const isns_tag_type_t *tag_type;
++		char		namebuf[64];
++		const char	*help;
++		unsigned int	i;
++
++		if (st && !st->multi_type_permitted
++		 && st->prefix && t->prefix != st->prefix)
++			continue;
++
++		tmpl = t->prefix->context;
++		if (tmpl != current) {
++			printf("\nAttributes for object type %s; using prefix %s\n",
++				tmpl->iot_name, t->prefix->name);
++			current = tmpl;
++		}
++
++		snprintf(namebuf, sizeof(namebuf), "%s%s", t->prefix->name, t->name);
++		printf("  %-20s   ", namebuf);
++
++		tag_type = isns_tag_type_by_id(t->tag);
++		if (tag_type == NULL) {
++			printf("Unknown\n");
++			continue;
++		}
++		printf("%s (%s", tag_type->it_name,
++				tag_type->it_type->it_name);
++
++		if (tag_type->it_readonly)
++			printf("; readonly");
++		if (tag_type->it_multiple)
++			printf("; multiple instances");
++		printf(")");
++
++		help = NULL;
++		if (t->tag == OPENISNS_TAG_POLICY_KEY) {
++			help = "name of key file, or \"gen\" for key generation";
++		} else
++		if (tag_type->it_help)
++			help = tag_type->it_help();
++
++		if (help) {
++			if (strlen(help) < 20)
++				printf(" [%s]", help);
++			else
++				printf("\n%25s[%s]", "", help);
++		}
++		printf("\n");
++
++		if (t->alias[0]) {
++			printf("%25sAliases:", "");
++			for (i = 0; i < MAX_ALIASES && t->alias[i]; ++i)
++				printf(" %s", t->alias[i]);
++			printf("\n");
++		}
++	}
++}
++
++isns_object_template_t *
++isns_attr_list_parser_context(const struct isns_attr_list_parser *st)
++{
++	if (st->prefix)
++		return st->prefix->context;
++	return NULL;
++}
++
++int
++isns_print_attrs(isns_object_t *obj, char **argv, unsigned int argsmax)
++{
++	struct isns_attr_list_parser st;
++	unsigned int	i, argc = 0;
++
++	isns_attr_list_parser_init(&st, obj->ie_template);
++
++	for (i = 0; i < obj->ie_attrs.ial_count; ++i) {
++		isns_attr_t *attr = obj->ie_attrs.ial_data[i];
++		char		argbuf[512], value[512];
++		const char	*name;
++
++		name = name_by_tag(attr->ia_tag_id, &st);
++		if (name == NULL)
++			continue;
++		if (argc + 1 >= argsmax)
++			break;
++
++		snprintf(argbuf, sizeof(argbuf), "%s%s=%s",
++				st.prefix->name, name,
++				isns_attr_print_value(attr, value, sizeof(value)));
++		argv[argc++] = isns_strdup(argbuf);
++	}
++
++	argv[argc] = NULL;
++	return argc;
++}
+diff --git a/utils/open-isns/getnext.c b/utils/open-isns/getnext.c
+new file mode 100644
+index 0000000..916ee80
+--- /dev/null
++++ b/utils/open-isns/getnext.c
+@@ -0,0 +1,257 @@
++/*
++ * Handle iSNS DevGetNext
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include "isns.h"
++#include "attrs.h"
++#include "message.h"
++#include "security.h"
++#include "objects.h"
++#include "db.h"
++#include "util.h"
++
++/*
++ * Create a GetNext query, and set the source name
++ */
++static isns_simple_t *
++__isns_create_getnext(isns_source_t *source,
++		const isns_attr_list_t *key,
++		const isns_attr_list_t *scope)
++{
++	isns_simple_t *simp;
++
++	simp = isns_simple_create(ISNS_DEVICE_GET_NEXT, source, key);
++	if (simp && scope)
++		isns_attr_list_copy(&simp->is_operating_attrs,
++				scope);
++	return simp;
++}
++
++isns_simple_t *
++isns_create_getnext(isns_client_t *clnt,
++		isns_object_template_t *tmpl,
++		const isns_attr_list_t *scope)
++{
++	isns_simple_t *simp;
++	unsigned int	i;
++
++	simp = __isns_create_getnext(clnt->ic_source, NULL, scope);
++	if (simp == NULL)
++		return NULL;
++
++	for (i = 0; i < tmpl->iot_num_keys; ++i) {
++		isns_attr_list_append_nil(&simp->is_message_attrs,
++				tmpl->iot_keys[i]);
++	}
++	return simp;
++}
++
++isns_simple_t *
++isns_create_getnext_followup(isns_client_t *clnt,
++		const isns_simple_t *resp,
++		const isns_attr_list_t *scope)
++{
++	return __isns_create_getnext(clnt->ic_source,
++			&resp->is_message_attrs, scope);
++}
++
++/*
++ * Get the list of objects matching this query
++ */
++static int
++isns_getnext_get_object(isns_simple_t *qry, isns_db_t *db,
++		isns_object_t **result)
++{
++	isns_scope_t		*scope;
++	isns_attr_list_t	*keys = &qry->is_message_attrs, match;
++	isns_object_template_t	*tmpl;
++	unsigned int		i;
++
++	/*
++	 * 5.6.5.3.
++	 * The Message Key Attribute may be an Entity Identifier (EID),
++	 * iSCSI Name, iSCSI Index, Portal IP Address and TCP/UDP Port,
++	 * Portal Index, PG Index, FC Node Name WWNN, or FC Port Name
++	 * WWPN.
++	 *
++	 * Implementer's comment: In other words, it must be the
++	 * key attr(s) of a specific object type, or an index attribute.
++	 */
++	if ((tmpl = isns_object_template_for_key_attrs(keys)) != NULL) {
++		if (keys->ial_count != tmpl->iot_num_keys)
++			return ISNS_INVALID_QUERY;
++	} else if (keys->ial_count == 1) {
++		isns_attr_t *attr = keys->ial_data[0];
++
++		tmpl = isns_object_template_for_index_tag(attr->ia_tag_id);
++	}
++	if (tmpl == NULL)
++		return ISNS_INVALID_QUERY;
++
++	/* Verify whether the client is permitted to retrieve
++	 * objects of the given type. */
++	if (!isns_policy_validate_object_type(qry->is_policy, tmpl,
++					qry->is_function))
++		return ISNS_SOURCE_UNAUTHORIZED;
++
++	/*
++	 * 5.6.5.3.
++	 * The Operating Attributes can be used to specify the scope
++	 * of the DevGetNext request, and to specify the attributes of
++	 * the next object, which are to be returned in the DevGetNext
++	 * response message.  All Operating Attributes MUST be attributes
++	 * of the object type identified by the Message Key.
++	 */
++	match = qry->is_operating_attrs;
++	for (i = 0; i < match.ial_count; ++i) {
++		isns_attr_t *attr = match.ial_data[i];
++
++		if (tmpl != isns_object_template_for_tag(attr->ia_tag_id))
++			return ISNS_INVALID_QUERY;
++	}
++
++	/*
++	 * 5.6.5.3.
++	 * Non-zero-length TLV attributes in the Operating Attributes
++	 * are used to scope the DevGetNext message.
++	 * [...]
++	 * Zero-length TLV attributes MUST be listed after non-zero-length
++	 * attributes in the Operating Attributes of the DevGetNext
++	 * request message.
++	 */
++	for (i = 0; i < match.ial_count; ++i) {
++		if (ISNS_ATTR_IS_NIL(match.ial_data[i])) {
++			match.ial_count = i;
++			break;
++		}
++	}
++
++	/* Get the scope for the originating node. */
++	scope = isns_scope_for_call(db, qry);
++
++	*result = isns_scope_get_next(scope, tmpl, keys, &match);
++
++	isns_scope_release(scope);
++
++	if (*result == NULL)
++		return ISNS_NO_SUCH_ENTRY;
++	return ISNS_SUCCESS;
++}
++
++/*
++ * Create a Query Response
++ */
++static isns_simple_t *
++isns_create_getnext_response(isns_source_t *source,
++		const isns_simple_t *qry, isns_object_t *obj)
++{
++	const isns_attr_list_t *req_attrs = NULL;
++	isns_attr_list_t requested;
++	isns_simple_t	*resp;
++	unsigned int	i;
++
++	resp = __isns_create_getnext(source, NULL, NULL);
++
++	/*
++	 * 5.7.5.3.  Device Get Next Response (DevGetNextRsp)
++	 * The Message Key Attribute field returns the object keys
++	 * for the next object after the Message Key Attribute in the
++	 * original DevGetNext message.
++	 *
++	 * Implementer's note: slightly convoluted English here. 
++	 * I *think* this means the key attributes of the object
++	 * we matched.
++	 */
++	if (!isns_object_get_key_attrs(obj, &resp->is_message_attrs))
++		return NULL;
++
++	/*
++	 * 5.7.5.3.
++	 * The Operating Attribute field returns the Operating Attributes
++	 * of the next object as requested in the original DevGetNext
++	 * message.  The values of the Operating Attributes are those
++	 * associated with the object identified by the Message Key
++	 * Attribute field of the DevGetNextRsp message.
++	 *
++	 * Implementer's note: the RFC doesn't say clearly what to
++	 * do when the list of operating attributes does not
++	 * contain any NIL TLVs. Let's default to the same
++	 * behavior as elsewhere, and return all attributes
++	 * in this case.
++	 */
++	req_attrs = &qry->is_operating_attrs;
++	for (i = 0; i < req_attrs->ial_count; ++i) {
++		if (ISNS_ATTR_IS_NIL(req_attrs->ial_data[i]))
++			break;
++	}
++	requested.ial_count = req_attrs->ial_count - i;
++	requested.ial_data = req_attrs->ial_data + i;
++	if (requested.ial_count)
++		req_attrs = &requested;
++	else
++		req_attrs = NULL;
++
++	isns_object_get_attrlist(obj,
++			&resp->is_operating_attrs,
++			req_attrs);
++	return resp;
++}
++
++/*
++ * Process a GetNext request
++ */
++int
++isns_process_getnext(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
++{
++	isns_simple_t		*reply = NULL;
++	isns_object_t		*obj = NULL;
++	isns_db_t		*db = srv->is_db;
++	int			status;
++
++	/* Get the next object */
++	status = isns_getnext_get_object(call, db, &obj);
++	if (status != ISNS_SUCCESS)
++		goto done;
++
++	/* If it's a virtual object, rebuild it */
++	if (obj->ie_rebuild)
++		obj->ie_rebuild(obj, srv->is_db);
++
++	/* Success: create a new simple message, and
++	 * send it in our reply. */
++	reply = isns_create_getnext_response(srv->is_source, call, obj);
++	if (reply == NULL)
++		status = ISNS_INTERNAL_ERROR;
++
++done:
++	if (obj)
++		isns_object_release(obj);
++	*result = reply;
++	return status;
++}
++
++/*
++ * Parse the object in a getnext response
++ */
++int
++isns_getnext_response_get_object(isns_simple_t *qry,
++		isns_object_t **result)
++{
++	isns_object_template_t *tmpl;
++
++	tmpl = isns_object_template_for_key_attrs(&qry->is_operating_attrs);
++	if (tmpl == NULL) {
++		isns_error("Cannot determine object type in GetNext response\n");
++		return ISNS_ATTRIBUTE_NOT_IMPLEMENTED;
++	}
++
++	*result = isns_create_object(tmpl,
++			&qry->is_operating_attrs,
++			NULL);
++	return ISNS_SUCCESS;
++}
++
+diff --git a/utils/open-isns/internal.h b/utils/open-isns/internal.h
+new file mode 100644
+index 0000000..fb80b48
+--- /dev/null
++++ b/utils/open-isns/internal.h
+@@ -0,0 +1,16 @@
++/*
++ * iSNS implementation - internal functions and types
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_INTERNAL_H
++#define ISNS_INTERNAL_H
++
++extern char *	isns_slp_build_url(uint16_t);
++extern int	isns_slp_register(const char *);
++extern int	isns_slp_unregister(const char *);
++extern char *	isns_slp_find(void);
++
++#endif /* ISNS_INTERNAL_H */
++
+diff --git a/utils/open-isns/isns-proto.h b/utils/open-isns/isns-proto.h
+new file mode 100644
+index 0000000..fbc3376
+--- /dev/null
++++ b/utils/open-isns/isns-proto.h
+@@ -0,0 +1,259 @@
++/*
++ * iSNS protocol definitions
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_PROTO_H
++#define ISNS_PROTO_H
++
++#include <stdint.h>
++
++struct isns_hdr {
++	uint16_t		i_version;
++	uint16_t		i_function;
++	uint16_t		i_length;
++	uint16_t		i_flags;
++	uint16_t		i_xid;
++	uint16_t		i_seq;
++};
++
++#define ISNS_VERSION		0x0001
++#define ISNS_MAX_PDU_SIZE	65535
++#define ISNS_DEFAULT_PORT	3205
++
++/*
++ * Values for the i_flags field:
++ */
++#define ISNS_F_CLIENT		0x8000
++#define ISNS_F_SERVER		0x4000
++#define ISNS_F_AUTHBLK_PRESENT	0x2000
++#define ISNS_F_REPLACE		0x1000
++#define ISNS_F_LAST_PDU		0x0800
++#define ISNS_F_FIRST_PDU	0x0400
++
++/*
++ * Function values
++ */
++enum isns_function {
++	ISNS_DEVICE_ATTRIBUTE_REGISTER	= 1,
++	ISNS_DEVICE_ATTRIBUTE_QUERY	= 2,
++	ISNS_DEVICE_GET_NEXT		= 3,
++	ISNS_DEVICE_DEREGISTER		= 4,
++	ISNS_SCN_REGISTER		= 5,
++	ISNS_SCN_DEREGISTER		= 6,
++	ISNS_SCN_EVENT			= 7,
++	ISNS_STATE_CHANGE_NOTIFICATION	= 8,
++	ISNS_DD_REGISTER		= 9,
++	ISNS_DD_DEREGISTER		= 10,
++	ISNS_DDS_REGISTER		= 11,
++	ISNS_DDS_DEREGISTER		= 12,
++	ISNS_ENTITY_STATUS_INQUIRY	= 13,
++	ISNS_HEARTBEAT			= 14,
++};
++
++/*
++ * iSNS status codes:
++ */
++enum isns_status {
++	ISNS_SUCCESS = 0,
++	ISNS_UNKNOWN_ERROR,
++	ISNS_MESSAGE_FORMAT_ERROR,
++	ISNS_INVALID_REGISTRATION,
++	__ISNS_RESERVED_STATUS,
++	ISNS_INVALID_QUERY,
++	ISNS_SOURCE_UNKNOWN,
++	ISNS_SOURCE_ABSENT,
++	ISNS_SOURCE_UNAUTHORIZED,
++	ISNS_NO_SUCH_ENTRY,
++	ISNS_VERSION_NOT_SUPPORTED,
++	ISNS_INTERNAL_ERROR,
++	ISNS_BUSY,
++	ISNS_OPTION_NOT_UNDERSTOOD,
++	ISNS_INVALID_UPDATE,
++	ISNS_MESSAGE_NOT_SUPPORTED,
++	ISNS_SCN_EVENT_REJECTED,
++	ISNS_SCN_REGISTRATION_REJECTED,
++	ISNS_ATTRIBUTE_NOT_IMPLEMENTED,
++	ISNS_FC_DOMAIN_ID_NOT_AVAILABLE,
++	ISNS_FC_DOMAIN_ID_NOT_ALLOCATED,
++	ISNS_ESI_NOT_AVAILABLE,
++	ISNS_INVALID_DEREGISTRATION,
++	ISNS_REGISTRATION_FEATURE_NOT_SUPPORTED,
++};
++
++enum isns_tag {
++	ISNS_TAG_DELIMITER		= 0,
++	ISNS_TAG_ENTITY_IDENTIFIER	= 1,
++	ISNS_TAG_ENTITY_PROTOCOL	= 2,
++	ISNS_TAG_MGMT_IP_ADDRESS	= 3,
++	ISNS_TAG_TIMESTAMP		= 4,
++	ISNS_TAG_PROTOCOL_VERSION_RANGE	= 5,
++	ISNS_TAG_REGISTRATION_PERIOD	= 6,
++	ISNS_TAG_ENTITY_INDEX		= 7,
++	ISNS_TAG_ENTITY_NEXT_INDEX	= 8,
++	ISNS_TAG_ENTITY_ISAKMP_PHASE_1	= 11,
++	ISNS_TAG_ENTITY_CERTIFICATE	= 12,
++	ISNS_TAG_PORTAL_IP_ADDRESS	= 16,
++	ISNS_TAG_PORTAL_TCP_UDP_PORT	= 17,
++	ISNS_TAG_PORTAL_SYMBOLIC_NAME	= 18,
++	ISNS_TAG_ESI_INTERVAL		= 19,
++	ISNS_TAG_ESI_PORT		= 20,
++	ISNS_TAG_PORTAL_INDEX		= 22,
++	ISNS_TAG_SCN_PORT		= 23,
++	ISNS_TAG_PORTAL_NEXT_INDEX	= 24,
++	ISNS_TAG_PORTAL_SECURITY_BITMAP	= 27,
++	ISNS_TAG_PORTAL_ISAKMP_PHASE_1	= 28,
++	ISNS_TAG_PORTAL_ISAKMP_PHASE_2	= 29,
++	ISNS_TAG_PORTAL_CERTIFICATE	= 31,
++	ISNS_TAG_ISCSI_NAME		= 32,
++	ISNS_TAG_ISCSI_NODE_TYPE	= 33,
++	ISNS_TAG_ISCSI_ALIAS		= 34,
++	ISNS_TAG_ISCSI_SCN_BITMAP	= 35,
++	ISNS_TAG_ISCSI_NODE_INDEX	= 36,
++	ISNS_TAG_WWNN_TOKEN		= 37,
++	ISNS_TAG_ISCSI_NODE_NEXT_INDEX	= 38,
++	ISNS_TAG_ISCSI_AUTHMETHOD	= 42,
++	ISNS_TAG_PG_ISCSI_NAME		= 48,
++	ISNS_TAG_PG_PORTAL_IP_ADDR	= 49,
++	ISNS_TAG_PG_PORTAL_TCP_UDP_PORT	= 50,
++	ISNS_TAG_PG_TAG			= 51,
++	ISNS_TAG_PG_INDEX		= 52,
++	ISNS_TAG_PG_NEXT_INDEX		= 53,
++	ISNS_TAG_FC_PORT_NAME_WWPN	= 64,
++	ISNS_TAG_PORT_ID		= 65,
++	ISNS_TAG_FC_PORT_TYPE		= 66,
++	ISNS_TAG_SYMBOLIC_PORT_NAME	= 67,
++	ISNS_TAG_FABRIC_PORT_NAME	= 68,
++	ISNS_TAG_HARD_ADDRESS		= 69,
++	ISNS_TAG_PORT_IP_ADDRESS	= 70,
++	ISNS_TAG_CLASS_OF_SERVICE	= 71,
++	ISNS_TAG_FC4_TYPES		= 72,
++	ISNS_TAG_FC4_DESCRIPTOR		= 73,
++	ISNS_TAG_FC4_FEATURES		= 74,
++	ISNS_TAG_IFCP_SCN_BITMAP	= 75,
++	ISNS_TAG_PORT_ROLE		= 76,
++	ISNS_TAG_PERMANENT_PORT_NAME	= 77,
++	ISNS_TAG_FC4_TYPE_CODE		= 95,
++	ISNS_TAG_FC_NODE_NAME_WWNN	= 96,
++	ISNS_TAG_SYMBOLIC_NODE_NAME	= 97,
++	ISNS_TAG_NODE_IP_ADDRESS	= 98,
++	ISNS_TAG_NODE_IPA		= 99,
++	ISNS_TAG_PROXY_ISCSI_NAME	= 101,
++	ISNS_TAG_SWITCH_NAME		= 128,
++	ISNS_TAG_PREFERRED_ID		= 129,
++	ISNS_TAG_ASSIGNED_ID		= 130,
++	ISNS_TAG_VIRTUAL_FABRIC_ID	= 131,
++	ISNS_TAG_SERVER_VENDOR_OUI	= 256,
++	ISNS_TAG_DD_SET_ID		= 2049,
++	ISNS_TAG_DD_SET_SYMBOLIC_NAME	= 2050,
++	ISNS_TAG_DD_SET_STATUS		= 2051,
++	ISNS_TAG_DD_SET_NEXT_ID		= 2052,
++	ISNS_TAG_DD_ID			= 2065,
++	ISNS_TAG_DD_SYMBOLIC_NAME	= 2066,
++	ISNS_TAG_DD_MEMBER_ISCSI_INDEX	= 2067,
++	ISNS_TAG_DD_MEMBER_ISCSI_NAME	= 2068,
++	ISNS_TAG_DD_MEMBER_FC_PORT_NAME	= 2069,
++	ISNS_TAG_DD_MEMBER_PORTAL_INDEX	= 2070,
++	ISNS_TAG_DD_MEMBER_PORTAL_IP_ADDR = 2071,
++	ISNS_TAG_DD_MEMBER_PORTAL_TCP_UDP_PORT = 2072,
++	ISNS_TAG_DD_FEATURES		= 2078,
++	ISNS_TAG_DD_NEXT_ID		= 2079,
++
++	__ISNS_TAG_MAX,
++
++	ISNS_VENDOR_SPECIFIC_SERVER_BASE = 257,		/* end 384 */
++	ISNS_VENDOR_SPECIFIC_ENTITY_BASE = 385,		/* end 512 */
++	ISNS_VENDOR_SPECIFIC_PORTAL_BASE = 513,		/* end 640 */
++	ISNS_VENDOR_SPECIFIC_NODE_BASE	= 641,		/* end 768 */
++	ISNS_VENDOR_SPECIFIC_DD_BASE	= 1024,		/* end 1280 */
++	ISNS_VENDOR_SPECIFIC_DDSET_BASE = 1281,		/* end 1536 */
++	ISNS_VENDOR_SPECIFIC_OTHER_BASE = 1537,		/* end 2048 */
++};
++
++typedef enum isns_entity_protocol {
++	ISNS_ENTITY_PROTOCOL_NONE = 1,
++	ISNS_ENTITY_PROTOCOL_ISCSI = 2,
++	ISNS_ENTITY_PROTOCOL_IFCP = 3,
++} isns_entity_protocol_t;
++
++enum isns_iscsi_node_type_bits {
++	ISNS_ISCSI_NODE_TYPE_TARGET = 0,
++	ISNS_ISCSI_NODE_TYPE_INITIATOR = 1,
++	ISNS_ISCSI_NODE_TYPE_CONTROL = 2,
++};
++#define ISNS_ISCSI_INITIATOR_MASK	(1 << ISNS_ISCSI_NODE_TYPE_INITIATOR)
++#define ISNS_ISCSI_TARGET_MASK		(1 << ISNS_ISCSI_NODE_TYPE_TARGET)
++#define ISNS_ISCSI_CONTROL_MASK		(1 << ISNS_ISCSI_NODE_TYPE_CONTROL)
++
++enum isns_portal_port_bits {
++	ISNS_PORTAL_PORT_UDP = 16,
++};
++#define ISNS_PORTAL_PORT_UDP_MASK	(1 << ISNS_PORTAL_PORT_UDP)
++
++enum isns_portal_security_bits {
++	ISNS_PORTAL_SEC_BITMAP_VALID = 0,
++	ISNS_PORTAL_SEC_IPSEC_ENABLED = 1,
++	ISNS_PORTAL_SEC_MAIN_MODE_ENABLED = 2,
++	ISNS_PORTAL_SEC_AGGR_MODE_ENABLED = 3,
++	ISNS_PORTAL_SEC_PFS_ENABLED = 4,
++	ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED = 5,
++	ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED = 6,
++};
++#define ISNS_PORTAL_SEC_BITMAP_VALID_MASK		(1 << ISNS_PORTAL_SEC_BITMAP_VALID)
++#define ISNS_PORTAL_SEC_IPSEC_ENABLED_MASK		(1 << ISNS_PORTAL_SEC_IPSEC_ENABLED)
++#define ISNS_PORTAL_SEC_MAIN_MODE_ENABLED_MASK		(1 << ISNS_PORTAL_SEC_MAIN_MODE_ENABLED)
++#define ISNS_PORTAL_SEC_AGGR_MODE_ENABLED_MASK		(1 << ISNS_PORTAL_SEC_AGGR_MODE_ENABLED)
++#define ISNS_PORTAL_SEC_PFS_ENABLED_MASK		(1 << ISNS_PORTAL_SEC_PFS_ENABLED)
++#define ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED_MASK	(1 << ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED)
++#define ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED_MASK	(1 << ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED)
++
++enum isns_scn_bits {
++	ISNS_SCN_DD_MEMBER_ADDED = 0,
++	ISNS_SCN_DD_MEMBER_REMOVED = 1,
++	ISNS_SCN_OBJECT_UPDATED = 2,
++	ISNS_SCN_OBJECT_ADDED = 3,
++	ISNS_SCN_OBJECT_REMOVED = 4,
++	ISNS_SCN_MANAGEMENT_REGISTRATION = 5,
++	ISNS_SCN_TARGET_AND_SELF_ONLY = 6,
++	ISNS_SCN_INITIATOR_AND_SELF_ONLY = 7,
++};
++#define ISNS_SCN_DD_MEMBER_ADDED_MASK		(1 << ISNS_SCN_DD_MEMBER_ADDED)
++#define ISNS_SCN_DD_MEMBER_REMOVED_MASK		(1 << ISNS_SCN_DD_MEMBER_REMOVED)
++#define ISNS_SCN_OBJECT_UPDATED_MASK		(1 << ISNS_SCN_OBJECT_UPDATED)
++#define ISNS_SCN_OBJECT_ADDED_MASK		(1 << ISNS_SCN_OBJECT_ADDED)
++#define ISNS_SCN_OBJECT_REMOVED_MASK		(1 << ISNS_SCN_OBJECT_REMOVED)
++#define ISNS_SCN_MANAGEMENT_REGISTRATION_MASK	(1 << ISNS_SCN_MANAGEMENT_REGISTRATION)
++#define ISNS_SCN_TARGET_AND_SELF_ONLY_MASK	(1 << ISNS_SCN_TARGET_AND_SELF_ONLY)
++#define ISNS_SCN_INITIATOR_AND_SELF_ONLY_MASK	(1 << ISNS_SCN_INITIATOR_AND_SELF_ONLY)
++
++enum isns_dds_status_bits {
++	ISNS_DDS_ENABLED = 0,
++};
++#define ISNS_DDS_ENABLED_MASK			(1 << ISNS_DDS_ENABLED)
++
++enum isns_dd_feature_bits {
++	ISNS_DD_BOOT_LIST_ENABLED = 0,
++};
++#define ISNS_DD_BOOT_LIST_ENABLED_MASK		(1 << ISN_BOOT_LIST_DDS_ENABLED)
++
++#define ISNS_PAD(len)		(((len) + 3) & ~3UL)
++
++/*
++ * iSNS auth block
++ */
++#define ISNS_AUTHBLK_SIZE	20
++struct isns_authblk {
++	uint32_t		iab_bsd;		/* 16bit in SLP */
++	uint32_t		iab_length;		/* 16bit in SLP */
++	uint64_t		iab_timestamp;		/* 32bit in SLP */
++	uint32_t		iab_spi_len;		/* 16bit in SLP */
++
++	char *			iab_spi;
++	void *			iab_sig;
++	uint32_t		iab_sig_len;
++} __attribute__((packed));
++
++#define ISNS_AUTH_TYPE_SHA1_DSA	0x0002
++
++#endif /* ISNS_PROTO_H */
+diff --git a/utils/open-isns/isns.h b/utils/open-isns/isns.h
+new file mode 100644
+index 0000000..53c22d5
+--- /dev/null
++++ b/utils/open-isns/isns.h
+@@ -0,0 +1,673 @@
++/*
++ * iSNS implementation - library header file.
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ *
++ * This file contains all declarations and definitions
++ * commonly required by users of libisns.
++ */
++
++#ifndef ISNS_H
++#define ISNS_H
++
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <stdio.h>
++
++#include <isns-proto.h>
++#include "types.h"
++
++#define ISNS_MAX_BUFFER		8192
++#define ISNS_MAX_MESSAGE	8192
++
++
++/*
++ * Client handle
++ */
++typedef struct isns_client isns_client_t;
++struct isns_client {
++	isns_source_t *	ic_source;
++	isns_socket_t *	ic_socket;
++};
++
++/*
++ * Server operations
++ */
++typedef int	isns_service_fn_t(isns_server_t *, isns_simple_t *, isns_simple_t **);
++typedef void	isns_scn_callback_fn_t(isns_db_t *, uint32_t scn_bits,
++					isns_object_template_t *node_type,
++					const char *node_name,
++					const char *recipient);
++struct isns_service_ops {
++	isns_service_fn_t *	process_registration;
++	isns_service_fn_t *	process_query;
++	isns_service_fn_t *	process_getnext;
++	isns_service_fn_t *	process_deregistration;
++	isns_service_fn_t *	process_scn_registration;
++	isns_service_fn_t *	process_scn_deregistration;
++	isns_service_fn_t *	process_scn_event;
++	isns_service_fn_t *	process_scn;
++	isns_service_fn_t *	process_dd_registration;
++	isns_service_fn_t *	process_dd_deregistration;
++	isns_service_fn_t *	process_esi;
++	isns_service_fn_t *	process_heartbeat;
++};
++
++extern struct isns_service_ops	isns_default_service_ops;
++extern struct isns_service_ops	isns_callback_service_ops;
++
++/*
++ * Output function
++ */
++void			isns_print_stdout(const char *, ...);
++
++/*
++ * Database events
++ */
++struct isns_db_event {
++	isns_object_t *		ie_recipient;	/* Recipient node or NULL */
++	isns_object_t *		ie_object;	/* Affected object */
++	isns_object_t *		ie_trigger;	/* Triggering object */
++	unsigned int		ie_bits;	/* SCN bitmask */
++};
++typedef void		isns_db_callback_t(const isns_db_event_t *,
++					void *user_data);
++
++/*
++ * Handling of client objects
++ */
++extern isns_client_t *	isns_create_default_client(isns_security_t *);
++extern isns_client_t *	isns_create_client(isns_security_t *,
++				const char *source_name);
++extern isns_client_t *	isns_create_local_client(isns_security_t *,
++				const char *source_name);
++extern int		isns_client_call(isns_client_t *,
++				isns_simple_t **inout);
++extern void		isns_client_destroy(isns_client_t *);
++extern int		isns_client_get_local_address(const isns_client_t *,
++				isns_portal_info_t *);
++
++/*
++ * Handling of server objects
++ */
++extern isns_server_t *	isns_create_server(isns_source_t *,
++				isns_db_t *,
++				struct isns_service_ops *);
++extern void		isns_server_set_scn_callback(isns_server_t *,
++				isns_scn_callback_fn_t *);
++
++
++/*
++ * Handling of source names
++ */
++extern int		isns_init_names(void);
++extern const char *	isns_default_source_name(void);
++extern isns_source_t *	isns_source_create(isns_attr_t *);
++extern isns_source_t *	isns_source_create_iscsi(const char *name);
++extern isns_source_t *	isns_source_create_ifcp(const char *name);
++extern uint32_t		isns_source_type(const isns_source_t *);
++extern const char *	isns_source_name(const isns_source_t *);
++extern isns_attr_t *	isns_source_attr(const isns_source_t *);
++extern isns_source_t *	isns_source_get(isns_source_t *);
++extern isns_source_t *	isns_source_from_object(const isns_object_t *);
++extern void		isns_source_release(isns_source_t *);
++extern int		isns_source_match(const isns_source_t *,
++				const isns_source_t *);
++
++extern void		isns_server_set_source(isns_source_t *);
++extern isns_message_t *	isns_process_message(isns_server_t *, isns_message_t *);
++
++extern void		isns_simple_print(isns_simple_t *,
++				isns_print_fn_t *);
++extern int		isns_simple_call(isns_socket_t *,
++				isns_simple_t **);
++extern int		isns_simple_transmit(isns_socket_t *,
++				isns_simple_t *,
++				const isns_portal_info_t *,
++				unsigned int,
++				void (*callback)(uint32_t, int,
++						 isns_simple_t *));
++extern void		isns_simple_free(isns_simple_t *);
++extern const isns_attr_list_t *isns_simple_get_attrs(isns_simple_t *);
++
++extern isns_simple_t *	isns_create_query(isns_client_t *clnt,
++				const isns_attr_list_t *query_key);
++extern isns_simple_t *	isns_create_query2(isns_client_t *clnt,
++				const isns_attr_list_t *query_key,
++				isns_source_t *source);
++extern int		isns_query_request_attr_tag(isns_simple_t *,
++				uint32_t);
++extern int		isns_query_request_attr(isns_simple_t *,
++				isns_attr_t *);
++extern int		isns_query_response_get_objects(isns_simple_t *qry,
++				isns_object_list_t *result);
++
++extern isns_simple_t *	isns_create_registration(isns_client_t *clnt,
++				isns_object_t *key_object);
++extern isns_simple_t *	isns_create_registration2(isns_client_t *clnt,
++				isns_object_t *key_object,
++				isns_source_t *source);
++extern void		isns_registration_set_replace(isns_simple_t *, int);
++extern void		isns_registration_add_object(isns_simple_t *,
++				isns_object_t *object);
++extern void		isns_registration_add_object_list(isns_simple_t *,
++				isns_object_list_t *);
++extern int		isns_registration_response_get_objects(isns_simple_t *,
++				isns_object_list_t *);
++
++extern isns_simple_t *	isns_create_getnext(isns_client_t *,
++				isns_object_template_t *,
++				const isns_attr_list_t *);
++extern int		isns_getnext_response_get_object(isns_simple_t *,
++				isns_object_t **);
++extern isns_simple_t *	isns_create_getnext_followup(isns_client_t *,
++				const isns_simple_t *,
++				const isns_attr_list_t *);
++
++extern isns_simple_t *	isns_create_deregistration(isns_client_t *clnt,
++				const isns_attr_list_t *);
++
++extern isns_simple_t *	isns_create_scn_registration(isns_client_t *clnt,
++				unsigned int);
++extern isns_simple_t *	isns_create_scn_registration2(isns_client_t *clnt,
++				unsigned int,
++				isns_source_t *);
++
++extern int		isns_dd_load_all(isns_db_t *);
++extern void		isns_dd_get_members(uint32_t, isns_object_list_t *, int);
++extern isns_simple_t *	isns_create_dd_registration(isns_client_t *,
++				const isns_attr_list_t *);
++extern isns_simple_t *	isns_create_dd_deregistration(isns_client_t *,
++				uint32_t, const isns_attr_list_t *);
++
++extern isns_object_t *	isns_create_object(isns_object_template_t *,
++				const isns_attr_list_t *,
++				isns_object_t *);
++extern isns_object_t *	isns_create_entity(int, const char *);
++extern isns_object_t *	isns_create_entity_for_source(const isns_source_t *,
++				const char *);
++extern const char *	isns_entity_name(const isns_object_t *);
++extern isns_object_t *	isns_create_portal(const isns_portal_info_t *,
++				isns_object_t *parent);
++extern isns_object_t *	isns_create_storage_node(const char *name,
++				uint32_t type_mask,
++				isns_object_t *parent);
++extern isns_object_t *	isns_create_storage_node2(const isns_source_t *,
++				uint32_t type_mask,
++				isns_object_t *parent);
++extern isns_object_t *	isns_create_iscsi_initiator(const char *name,
++				isns_object_t *parent);
++extern isns_object_t *	isns_create_iscsi_target(const char *name,
++				isns_object_t *parent);
++extern const char *	isns_storage_node_name(const isns_object_t *);
++extern isns_attr_t *	isns_storage_node_key_attr(const isns_object_t *);
++extern isns_object_t *	isns_create_portal_group(isns_object_t *portal,
++				isns_object_t *iscsi_node, uint32_t pg_tag);
++extern isns_object_t *	isns_create_default_portal_group(isns_db_t *,
++				isns_object_t *portal,
++				isns_object_t *node);
++extern void		isns_get_portal_groups(isns_object_t *portal,
++				isns_object_t *node,
++				isns_object_list_t *result);
++
++extern const char *	isns_object_template_name(isns_object_template_t *);
++extern int		isns_object_set_attr(isns_object_t *, isns_attr_t *);
++extern int		isns_object_set_attrlist(isns_object_t *, const isns_attr_list_t *);
++extern isns_object_t *	isns_object_get(isns_object_t *);
++extern int		isns_object_get_attrlist(isns_object_t *obj,
++				isns_attr_list_t *result,
++				const isns_attr_list_t *requested_attrs);
++extern int		isns_object_get_key_attrs(isns_object_t *,
++				isns_attr_list_t *);
++extern int		isns_object_get_attr(const isns_object_t *, uint32_t,
++				isns_attr_t **);
++extern void		isns_object_get_related(isns_db_t *,
++				isns_object_t *, isns_object_list_t *);
++extern void		isns_object_get_descendants(const isns_object_t *,
++				isns_object_template_t *,
++				isns_object_list_t *);
++extern void		isns_object_release(isns_object_t *);
++extern int		isns_object_match(const isns_object_t *,
++				const isns_attr_list_t *);
++extern isns_object_t *	isns_object_get_entity(isns_object_t *);
++extern int		isns_object_attr_valid(isns_object_template_t *, uint32_t);
++extern int		isns_object_contains(const isns_object_t *, const isns_object_t *);
++extern int		isns_object_delete_attr(isns_object_t *, uint32_t);
++extern int		isns_object_is(const isns_object_t *,
++				isns_object_template_t *);
++extern int		isns_object_is_entity(const isns_object_t *);
++extern int		isns_object_is_iscsi_node(const isns_object_t *);
++extern int		isns_object_is_fc_port(const isns_object_t *);
++extern int		isns_object_is_fc_node(const isns_object_t *);
++extern int		isns_object_is_portal(const isns_object_t *);
++extern int		isns_object_is_pg(const isns_object_t *);
++extern int		isns_object_is_policy(const isns_object_t *);
++extern int		isns_object_is_dd(const isns_object_t *);
++extern int		isns_object_is_ddset(const isns_object_t *);
++extern void		isns_object_print(isns_object_t *,
++				isns_print_fn_t *);
++extern time_t		isns_object_last_modified(const isns_object_t *);
++extern int		isns_object_mark_membership(isns_object_t *, uint32_t);
++extern int		isns_object_clear_membership(isns_object_t *, uint32_t);
++extern int		isns_object_test_membership(const isns_object_t *, uint32_t);
++extern int		isns_object_test_visibility(const isns_object_t *,
++				const isns_object_t *);
++extern void		isns_object_get_visible(const isns_object_t *,
++				isns_db_t *, isns_object_list_t *);
++extern void		isns_entity_touch(isns_object_t *);
++extern int		isns_object_extract_keys(const isns_object_t *,
++				isns_attr_list_t *);
++extern int		isns_object_extract_all(const isns_object_t *,
++				isns_attr_list_t *);
++extern int		isns_object_extract_writable(const isns_object_t *,
++				isns_attr_list_t *);
++
++
++extern int		isns_object_set_nil(isns_object_t *obj,	
++				uint32_t tag);
++extern int		isns_object_set_string(isns_object_t *obj,	
++				uint32_t tag,
++				const char *value);
++extern int		isns_object_set_uint32(isns_object_t *obj,	
++				uint32_t tag,
++				uint32_t value);
++extern int		isns_object_set_uint64(isns_object_t *obj,	
++				uint32_t tag,
++				uint64_t value);
++extern int		isns_object_set_ipaddr(isns_object_t *obj,	
++				uint32_t tag,
++				const struct in6_addr *value);
++
++extern int		isns_object_get_string(const isns_object_t *,
++				uint32_t,
++				const char **);
++extern int		isns_object_get_ipaddr(const isns_object_t *,
++				uint32_t,
++				struct in6_addr *);
++extern int		isns_object_get_uint32(const isns_object_t *,
++				uint32_t,
++				uint32_t *);
++extern int		isns_object_get_uint64(const isns_object_t *,
++				uint32_t,
++				uint64_t *);
++extern int		isns_object_get_opaque(const isns_object_t *,
++				uint32_t,
++				const void **, size_t *);
++
++
++extern int		isns_object_find_descendants(isns_object_t *obj,
++				isns_object_template_t *,
++				const isns_attr_list_t *keys,
++				isns_object_list_t *result);
++extern isns_object_t *	isns_object_find_descendant(isns_object_t *obj,
++				const isns_attr_list_t *keys);
++extern int		isns_object_detach(isns_object_t *);
++extern int		isns_object_attach(isns_object_t *, isns_object_t *);
++extern void		isns_object_prune_attrs(isns_object_t *);
++extern void		isns_mark_object(isns_object_t *, unsigned int);
++
++extern int		isns_get_entity_identifier(isns_object_t *, const char **);
++extern int		isns_get_entity_protocol(isns_object_t *, isns_entity_protocol_t *);
++extern int		isns_get_entity_index(isns_object_t *, uint32_t *);
++
++extern int		isns_get_portal_ipaddr(isns_object_t *, struct in6_addr *);
++extern int		isns_get_portal_tcpudp_port(isns_object_t *,
++				int *ipprotocol, uint16_t *port);
++extern int		isns_get_portal_index(isns_object_t *, uint32_t *);
++
++extern int		isns_get_address(struct sockaddr_storage *,
++				const char *, const char *, int, int, int);
++extern char *		isns_get_canon_name(const char *);
++
++extern isns_db_t *	isns_db_open(const char *location);
++extern isns_db_t *	isns_db_open_shadow(isns_object_list_t *);
++extern isns_object_t *	isns_db_lookup(isns_db_t *,
++				isns_object_template_t *,
++				const isns_attr_list_t *);
++extern isns_object_t *	isns_db_vlookup(isns_db_t *,
++				isns_object_template_t *,
++				...);
++extern int		isns_db_gang_lookup(isns_db_t *,
++				isns_object_template_t *,
++				const isns_attr_list_t *,
++				isns_object_list_t *);
++extern isns_object_t *	isns_db_get_next(isns_db_t *,
++				isns_object_template_t *,
++				const isns_attr_list_t *current,
++				const isns_attr_list_t *scope,
++				const isns_source_t *source);
++extern isns_object_t *	isns_db_lookup_source_node(isns_db_t *,
++				const isns_source_t *);
++extern void		isns_db_get_domainless(isns_db_t *,
++				isns_object_template_t *,
++				isns_object_list_t *);
++extern uint32_t		isns_db_allocate_index(isns_db_t *);
++extern void		isns_db_insert(isns_db_t *, isns_object_t *);
++extern void		isns_db_insert_limbo(isns_db_t *, isns_object_t *);
++extern int		isns_db_remove(isns_db_t *, isns_object_t *);
++extern time_t		isns_db_expire(isns_db_t *);
++extern void		isns_db_purge(isns_db_t *);
++extern void		isns_db_sync(isns_db_t *);
++extern const char *	isns_db_generate_eid(isns_db_t *, char *, size_t);
++extern isns_object_t *	isns_db_get_control(isns_db_t *);
++extern void		isns_db_print(isns_db_t *,
++				isns_print_fn_t *);
++
++extern void		isns_db_begin_transaction(isns_db_t *);
++extern void		isns_db_commit(isns_db_t *);
++extern void		isns_db_rollback(isns_db_t *);
++
++extern void		isns_object_event(isns_object_t *obj,
++				unsigned int bits,
++				isns_object_t *trigger);
++extern void		isns_unicast_event(isns_object_t *dst,
++				isns_object_t *obj,
++				unsigned int bits,
++				isns_object_t *trigger);
++extern void		isns_register_callback(isns_db_callback_t *,
++				void *);
++extern void		isns_flush_events(void);
++extern const char *	isns_event_string(unsigned int);
++
++extern void		isns_add_timer(unsigned int,
++				isns_timer_callback_t *, void *);
++extern void		isns_add_oneshot_timer(unsigned int,
++				isns_timer_callback_t *, void *);
++extern void		isns_cancel_timer(isns_timer_callback_t *, void *);
++extern time_t		isns_run_timers(void);
++
++extern void		isns_object_list_init(isns_object_list_t *);
++extern void		isns_object_list_destroy(isns_object_list_t *);
++extern int		isns_object_list_contains(const isns_object_list_t *,
++				isns_object_t *);
++extern void		isns_object_list_append(isns_object_list_t *,
++				isns_object_t *);
++extern void		isns_object_list_append_list(isns_object_list_t *,
++				const isns_object_list_t *);
++extern isns_object_t *	isns_object_list_lookup(const isns_object_list_t *,
++				isns_object_template_t *,
++				const isns_attr_list_t *);
++extern int		isns_object_list_gang_lookup(const isns_object_list_t *,
++				isns_object_template_t *,
++				const isns_attr_list_t *,
++				isns_object_list_t *);
++extern int		isns_object_list_remove(isns_object_list_t *,
++				isns_object_t *);
++extern void		isns_object_list_uniq(isns_object_list_t *);
++extern void		isns_object_list_print(const isns_object_list_t *,
++				isns_print_fn_t *);
++
++isns_object_template_t *isns_object_template_for_key_attrs(const isns_attr_list_t *);
++isns_object_template_t *isns_object_template_for_tag(uint32_t);
++isns_object_template_t *isns_object_template_for_index_tag(uint32_t);
++isns_object_template_t *isns_object_template_find(uint32_t);
++
++extern int		isns_attr_set(isns_attr_t *, const void *);
++extern isns_attr_t *	isns_attr_get(isns_attr_t *);
++extern void		isns_attr_release(isns_attr_t *);
++extern void		isns_attr_print(const isns_attr_t *,
++				isns_print_fn_t *);
++extern char *		isns_attr_print_value(const isns_attr_t *,
++				char *, size_t);
++extern int		isns_attr_match(const isns_attr_t *,
++				const isns_attr_t *);
++extern int		isns_attr_compare(const isns_attr_t *,
++				const isns_attr_t *);
++extern isns_attr_t *	isns_attr_from_string(uint32_t, const char *);
++
++extern void		isns_attr_list_print(const isns_attr_list_t *,
++				isns_print_fn_t *);
++
++extern void		isns_attr_list_init(isns_attr_list_t *);
++extern void		isns_attr_list_copy(isns_attr_list_t *,
++				const isns_attr_list_t *);
++extern void		isns_attr_list_destroy(isns_attr_list_t *);
++extern int		isns_attr_list_remove_tag(isns_attr_list_t *,
++				uint32_t);
++
++extern void		isns_attr_list_append_attr(isns_attr_list_t *,
++				isns_attr_t *);
++extern void		isns_attr_list_append_list(isns_attr_list_t *,
++				const isns_attr_list_t *);
++extern int		isns_attr_list_replace_attr(isns_attr_list_t *,
++				isns_attr_t *);
++/* Warning: this does *NOT* return a reference to the attribute */
++extern int		isns_attr_list_get_attr(const isns_attr_list_t *,
++				uint32_t tag,
++				isns_attr_t **);
++
++extern void		isns_attr_list_append_nil(isns_attr_list_t *,
++				uint32_t tag);
++extern void		isns_attr_list_append_string(isns_attr_list_t *,
++				uint32_t tag, const char *value);
++extern void		isns_attr_list_append_uint32(isns_attr_list_t *,
++				uint32_t tag, uint32_t value);
++extern void		isns_attr_list_append_uint64(isns_attr_list_t *,
++				uint32_t, int64_t);
++extern void		isns_attr_list_append_int32(isns_attr_list_t *,
++				uint32_t tag, int32_t value);
++extern void		isns_attr_list_append_opaque(isns_attr_list_t *,
++				uint32_t tag, const void *ptr, size_t len);
++extern void		isns_attr_list_append_ipaddr(isns_attr_list_t *,
++				uint32_t tag, const struct in6_addr *);
++
++extern int		isns_attr_list_append(isns_attr_list_t *,
++				uint32_t tag, const void *);
++extern int		isns_attr_list_update(isns_attr_list_t *,
++				uint32_t tag, const void *);
++
++extern int		isns_attr_list_contains(const isns_attr_list_t *,
++				uint32_t tag);
++extern int		isns_attr_list_compare(const isns_attr_list_t *,
++				const isns_attr_list_t *);
++
++/*
++ * Helper macros
++ */
++#define ISNS_ATTR_TYPE_CHECK(attr, type) \
++		((attr)->ia_value.iv_type == &isns_attr_type_##type)
++#define ISNS_ATTR_IS_NIL(attr) \
++		ISNS_ATTR_TYPE_CHECK(attr, nil)
++#define ISNS_ATTR_IS_STRING(attr) \
++		ISNS_ATTR_TYPE_CHECK(attr, string)
++#define ISNS_ATTR_IS_IPADDR(attr) \
++		ISNS_ATTR_TYPE_CHECK(attr, ipaddr)
++#define ISNS_ATTR_IS_UINT32(attr) \
++		ISNS_ATTR_TYPE_CHECK(attr, uint32)
++#define ISNS_ATTR_IS_UINT64(attr) \
++		ISNS_ATTR_TYPE_CHECK(attr, uint64)
++#define ISNS_ATTR_IS_OPAQUE(attr) \
++		ISNS_ATTR_TYPE_CHECK(attr, opaque)
++
++
++
++extern isns_socket_t *	isns_create_server_socket(const char *hostname, const char *portname,
++				int af_hint, int sock_type);
++extern isns_socket_t *	isns_create_client_socket(const char *hostname, const char *portname,
++				int af_hint, int sock_type);
++extern isns_socket_t *	isns_create_bound_client_socket(const char *myaddr,
++				const char *hostname, const char *portname,
++				int af_hint, int sock_type);
++extern isns_socket_t *	isns_connect_to_portal(const isns_portal_info_t *);
++extern void		isns_socket_set_report_failure(isns_socket_t *);
++extern void		isns_socket_set_disconnect_fatal(isns_socket_t *);
++extern int		isns_socket_get_local_addr(const isns_socket_t *,
++				struct sockaddr_storage *);
++extern int		isns_socket_get_portal_info(const isns_socket_t *,
++				isns_portal_info_t *);
++extern void		isns_socket_set_security_ctx(isns_socket_t *,
++				isns_security_t *);
++extern isns_message_t *	isns_recv_message(struct timeval *timeout);
++extern isns_message_t *	isns_socket_call(isns_socket_t *, isns_message_t *, long);
++extern int		isns_socket_send(isns_socket_t *, isns_message_t *);
++extern void		isns_socket_free(isns_socket_t *);
++extern int		isns_addr_get_port(const struct sockaddr *);
++extern void		isns_addr_set_port(struct sockaddr *, unsigned int);
++extern isns_socket_t *	isns_socket_find_server(const isns_portal_info_t *);
++
++extern isns_message_t *	isns_create_message(uint16_t function, uint16_t flags);
++extern isns_message_t *	isns_create_reply(const isns_message_t *);
++extern int		isns_message_init(isns_message_t *,
++				uint16_t, uint16_t, size_t);
++extern int		isns_message_status(isns_message_t *);
++extern void		isns_message_release(isns_message_t *);
++extern unsigned int	isns_message_function(const isns_message_t *);
++extern isns_socket_t *	isns_message_socket(const isns_message_t *);
++extern void		isns_message_set_error(isns_message_t *, uint32_t);
++
++extern const char *	isns_strerror(enum isns_status);
++extern const char *	isns_function_name(unsigned int);
++
++/*
++ * Security related functions
++ */
++extern int		isns_security_init(void);
++extern isns_principal_t *isns_security_load_privkey(isns_security_t *,
++				const char *filename);
++extern isns_principal_t *isns_security_load_pubkey(isns_security_t *,
++				const char *filename);
++extern isns_security_t *isns_default_security_context(int server_only);
++extern isns_security_t *isns_control_security_context(int server_only);
++extern isns_security_t *isns_create_dsa_context(void);
++extern void		isns_security_set_identity(isns_security_t *, isns_principal_t *);
++extern void		isns_principal_free(isns_principal_t *);
++extern void		isns_add_principal(isns_security_t *, isns_principal_t *);
++extern isns_keystore_t *isns_create_keystore(const char *);
++extern void		isns_security_set_keystore(isns_security_t *,
++				isns_keystore_t *);
++extern void		isns_principal_set_name(isns_principal_t *, const char *);
++extern const char *	isns_principal_name(const isns_principal_t *);
++
++extern isns_object_template_t	isns_entity_template;
++extern isns_object_template_t	isns_portal_template;
++extern isns_object_template_t	isns_iscsi_node_template;
++extern isns_object_template_t	isns_fc_port_template;
++extern isns_object_template_t	isns_fc_node_template;
++extern isns_object_template_t	isns_iscsi_pg_template;
++extern isns_object_template_t	isns_dd_template;
++extern isns_object_template_t	isns_ddset_template;
++
++/*
++ * Config file parser
++ */
++struct isns_config {
++	char *		ic_host_name;
++	char *		ic_auth_name;
++	char *		ic_source_name;
++	char *		ic_source_suffix;
++	char *		ic_entity_name;
++
++	char *		ic_server_name;
++	char *		ic_bind_address;
++	char *		ic_database;
++	char *		ic_auth_key_file;
++	char *		ic_server_key_file;
++	char *		ic_client_keystore;
++	char *		ic_control_socket;
++	char *		ic_pidfile;
++	char *		ic_local_registry_file;
++	int		ic_security;
++	int		ic_slp_register;
++
++	char *		ic_control_name;
++	char *		ic_control_key_file;
++
++	unsigned int	ic_registration_period;
++	unsigned int	ic_scn_timeout;
++	unsigned int	ic_scn_retries;
++	char *		ic_scn_callout;
++
++	unsigned int	ic_esi_max_interval;
++	unsigned int	ic_esi_min_interval;
++	unsigned int	ic_esi_retries;
++
++	unsigned int	ic_use_default_domain;
++
++	struct {
++	   unsigned int	policy;
++	   unsigned int	replay_window;
++	   unsigned int	timestamp_jitter;
++	   int		allow_unknown_peers;
++	}		ic_auth;
++	struct {
++	   unsigned int	max_sockets;
++	   unsigned int	connect_timeout;
++	   unsigned int	reconnect_timeout;
++	   unsigned int	call_timeout;
++	   unsigned int	udp_retrans_timeout;
++	   unsigned int	tcp_retrans_timeout;
++	   unsigned int	idle_timeout;
++	} ic_network;
++	struct {
++	   char *	param_file;
++	   unsigned int	key_bits;
++	} ic_dsa;
++
++};
++
++extern struct isns_config isns_config;
++extern int		isns_read_config(const char *);
++extern int		isns_config_set(const char *, char *);
++
++/*
++ * Reserved entity name for Policy information
++ */
++#define ISNS_ENTITY_CONTROL	"CONTROL"
++
++
++/*
++ * Helpers to deal with portal information
++ */
++struct isns_portal_info {
++	struct sockaddr_in6	addr;
++	int			proto;
++};
++
++extern void		isns_portal_init(isns_portal_info_t *,
++				const struct sockaddr *, int);
++extern int		isns_portal_parse(isns_portal_info_t *portal,
++				const char *addr_spec,
++				const char *default_port);
++extern int		isns_portal_from_attr_list(isns_portal_info_t *,
++				uint32_t addr_tag, uint32_t port_tag,
++				const isns_attr_list_t *);
++extern int		isns_portal_from_attr_pair(isns_portal_info_t *,
++				const isns_attr_t *,
++				const isns_attr_t *);
++extern int		isns_portal_from_object(isns_portal_info_t *,
++				uint32_t addr_tag, uint32_t port_tag,
++				const isns_object_t *);
++extern int		isns_portal_from_sockaddr(isns_portal_info_t *,
++				const struct sockaddr_storage *);
++extern int		isns_portal_to_sockaddr(const isns_portal_info_t *,
++				struct sockaddr_storage *);
++extern int		isns_portal_to_attr_list(const isns_portal_info_t *,
++				uint32_t addr_tag, uint32_t port_tag,
++				isns_attr_list_t *);
++extern int		isns_portal_to_object(const isns_portal_info_t *,
++				uint32_t addr_tag, uint32_t port_tag,
++				isns_object_t *);
++extern int		isns_portal_is_wildcard(const isns_portal_info_t *);
++extern uint32_t		isns_portal_tcpudp_port(const isns_portal_info_t *);
++extern const char *	isns_portal_string(const isns_portal_info_t *);
++extern int		isns_portal_equal(const isns_portal_info_t *,
++				const isns_portal_info_t *);
++extern int		isns_enumerate_portals(isns_portal_info_t *,
++				unsigned int);
++extern int		isns_get_nr_portals(void);
++
++/* Local registry stuff */
++extern int		isns_local_registry_load(const char *, pid_t, isns_object_list_t *);
++extern int		isns_local_registry_store(const char *, pid_t, const isns_object_list_t *);
++extern int		isns_local_registry_purge(const char *, pid_t);
++
++/* Should go somwhere else .*/
++extern int		isns_esi_enabled;
++
++extern void		isns_esi_init(isns_server_t *);
++extern void		isns_esi_register(isns_object_t *);
++
++extern void		isns_scn_init(isns_server_t *);
++extern time_t		isns_scn_transmit_all(void);
++
++#endif /* ISNS_H */
+diff --git a/utils/open-isns/isnsadm.c b/utils/open-isns/isnsadm.c
+new file mode 100644
+index 0000000..db34f8f
+--- /dev/null
++++ b/utils/open-isns/isnsadm.c
+@@ -0,0 +1,1151 @@
++/*
++ * isnsadm - helper utility
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <getopt.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <limits.h>
++
++#include "isns.h"
++#include "util.h"
++#include "vendor.h"
++#include "attrs.h"
++#include "security.h"
++#include "objects.h"
++#include "paths.h"
++#include "config.h"
++
++#define ISNS_DEFAULT_PORT_INITIATOR	860
++#define ISNS_DEFAULT_PORT_TARGET	3260
++
++
++enum {
++	DO_REGISTER = 1024,
++	DO_QUERY,
++	DO_QUERY_EID,
++	DO_LIST,
++	DO_DEREGISTER,
++	DO_DD_REGISTER,
++	DO_DD_DEREGISTER,
++	DO_ENROLL,
++	DO_EDIT_POLICY,
++	DO_DELETE_POLICY,
++};
++
++static struct option	options[] = {
++      { "help",			no_argument,		NULL,	'h'		},
++      {	"config",		required_argument,	NULL,	'c'		},
++      {	"debug",		required_argument,	NULL,	'd'		},
++      { "keyfile",		required_argument,	NULL,	'K',		},
++      { "key",			required_argument,	NULL,	'k',		},
++      {	"local",		no_argument,		NULL,	'l'		},
++      {	"control",		no_argument,		NULL,	'C'		},
++      {	"replace",		no_argument,		NULL,	'r'		},
++      { "query",		no_argument,		NULL,	DO_QUERY	},
++      { "query-eid",		no_argument,		NULL,	DO_QUERY_EID	},
++      { "list",			no_argument,		NULL,	DO_LIST		},
++      { "register",		no_argument,		NULL,	DO_REGISTER	},
++      {	"deregister",		no_argument,		NULL,	DO_DEREGISTER	},
++      { "dd-register",		no_argument,		NULL,	DO_DD_REGISTER	},
++      {	"dd-deregister",	no_argument,		NULL,	DO_DD_DEREGISTER},
++
++      { "enroll",		no_argument,		NULL,	DO_ENROLL	},
++      { "edit-policy",		no_argument,		NULL,	DO_EDIT_POLICY	},
++      { "delete-policy",	no_argument,		NULL,	DO_DELETE_POLICY },
++
++      { "version",		no_argument,		NULL,	'V'		},
++      { NULL }
++};
++
++
++static const char *	opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG;
++static int		opt_af = AF_UNSPEC;
++static int		opt_action = 0;
++static int		opt_local = 0;
++static int		opt_control = 0;
++static int		opt_replace = 0;
++static const char *	opt_keyfile = NULL;
++static char *		opt_key = NULL;
++static struct sockaddr_storage opt_myaddr;
++
++static void	usage(int, const char *);
++
++static int	register_objects(isns_client_t *, int, char **);
++static int	query_objects(isns_client_t *, int, char **);
++static int	query_entity_id(isns_client_t *, int, char **);
++static int	list_objects(isns_client_t *, int, char **);
++static int	deregister_objects(isns_client_t *, int, char **);
++static int	register_domain(isns_client_t *, int, char **);
++static int	deregister_domain(isns_client_t *, int, char **);
++static int	enroll_client(isns_client_t *, int, char **);
++static int	edit_policy(isns_client_t *, int, char **);
++
++static isns_attr_t *	load_key_callback(const char *);
++static isns_attr_t *	generate_key_callback(void);
++
++int
++main(int argc, char **argv)
++{
++	isns_client_t	*clnt;
++	isns_security_t	*security = NULL;
++	int		c, status;
++
++	while ((c = getopt_long(argc, argv, "46Cc:d:hK:k:l", options, NULL)) != -1) {
++		switch (c) {
++		case '4':
++			opt_af = AF_INET;
++			break;
++
++		case '6':
++			opt_af = AF_INET6;
++			break;
++
++		case 'C':
++			opt_control = 1;
++			break;
++
++		case 'c':
++			opt_configfile = optarg;
++			break;
++
++		case 'd':
++			isns_enable_debugging(optarg);
++			break;
++
++		case 'h':
++			usage(0, NULL);
++			break;
++
++		case 'K':
++			opt_keyfile = optarg;
++			break;
++
++		case 'k':
++			opt_key = optarg;
++			break;
++
++		case 'l':
++			opt_local = 1;
++			break;
++
++		case 'r':
++			opt_replace = 1;
++			break;
++
++		case 'V':
++			printf("Open-iSNS version %s\n"
++			       "Copyright (C) 2007, Olaf Kirch <olaf.kirch@oracle.com>\n",
++			       OPENISNS_VERSION_STRING);
++			return 0;
++
++		case DO_REGISTER:
++		case DO_QUERY:
++		case DO_QUERY_EID:
++		case DO_LIST:
++		case DO_DEREGISTER:
++		case DO_DD_REGISTER:
++		case DO_DD_DEREGISTER:
++		case DO_ENROLL:
++		case DO_EDIT_POLICY:
++		case DO_DELETE_POLICY:
++			if (opt_action)
++				usage(1, "You cannot specify more than one mode\n");
++			opt_action = c;
++			break;
++
++		default:
++			usage(1, "Unknown option");
++		}
++	}
++	
++	isns_read_config(opt_configfile);
++
++	if (!isns_config.ic_source_name)
++		usage(1, "Please specify an iSNS source name");
++	if (!isns_config.ic_server_name)
++		usage(1, "Please specify an iSNS server name");
++	if (!opt_action)
++		usage(1, "Please specify an operating mode");
++
++	if (opt_control) {
++		if (!isns_config.ic_security)
++			isns_fatal("Cannot use control mode, security disabled\n");
++		security = isns_control_security_context(0);
++		if (!security)
++			isns_fatal("Unable to create control security context\n");
++
++		/* Create a networked client, using isns.control as
++		 * the source name */
++		clnt = isns_create_client(security, isns_config.ic_control_name);
++	} else if (opt_local) {
++		/* Create a local client, using isns.control as
++		 * the source name */
++		clnt = isns_create_local_client(security,
++				isns_config.ic_control_name);
++	} else {
++		/* Create a networked client, using the configured
++		 * source name */
++		clnt = isns_create_default_client(security);
++	}
++
++	if (clnt == NULL)
++		return 1;
++
++	/* We're an interactive app, and don't want to retry
++	 * forever if the server refuses us. */
++	isns_socket_set_disconnect_fatal(clnt->ic_socket);
++
++	/* Get the IP address we use to talk to the iSNS server */
++	if (opt_myaddr.ss_family == AF_UNSPEC && !opt_local) {
++		if (!isns_socket_get_local_addr(clnt->ic_socket, &opt_myaddr))
++			isns_fatal("Unable to obtain my IP address\n");
++		isns_addr_set_port((struct sockaddr *) &opt_myaddr, 860);
++	}
++
++	argv += optind; argc -= optind;
++	switch (opt_action) {
++	case DO_REGISTER:
++		status = register_objects(clnt, argc, argv);
++		break;
++
++	case DO_QUERY:
++		status = query_objects(clnt, argc, argv);
++		break;
++
++	case DO_QUERY_EID:
++		status = query_entity_id(clnt, argc, argv);
++		break;
++
++	case DO_LIST:
++		status = list_objects(clnt, argc, argv);
++		break;
++
++	case DO_DEREGISTER:
++		status = deregister_objects(clnt, argc, argv);
++		break;
++
++	case DO_DD_REGISTER:
++		status = register_domain(clnt, argc, argv);
++		break;
++
++	case DO_DD_DEREGISTER:
++		status = deregister_domain(clnt, argc, argv);
++		break;
++
++
++	case DO_ENROLL:
++		status = enroll_client(clnt, argc, argv);
++		break;
++
++	case DO_EDIT_POLICY:
++		status = edit_policy(clnt, argc, argv);
++		break;
++
++	// case DO_DELETE_POLICY:
++
++	default:
++		isns_fatal("Not yet implemented\n");
++		status = 1; /* compiler food */
++	}
++
++	return status != ISNS_SUCCESS;
++}
++
++void
++usage(int exval, const char *msg)
++{
++	if (msg)
++		fprintf(stderr, "Error: %s\n", msg);
++	fprintf(stderr,
++	"Usage: isnsadm [options] --action ...\n"
++	"  --config        Specify alternative config fille\n"
++	"  --debug         Enable debugging (list of debug flags)\n"
++	"  --keyfile       Where to store newly generated private key\n"
++	"  --local         Use local Unix socket to talk to isnsd\n"
++	"  --control       Assume control node identity for authentication\n"
++	"  --replace       Use replace mode (--register only)\n"
++	"\nThe following actions are supported:\n"
++	"  --register      Register one or more objects\n"
++	"  --deregister    Deregister an object (and children)\n"
++	"  --dd-register   Register a Discovery Domain (and members)\n"
++	"  --dd-deregister Deregister a Discovery Domain (and members)\n"
++	"  --query         Query iSNS server for objects\n"
++	"  --list          List all objects of a given type\n"
++	"  --enroll        Create a new policy object for a client\n"
++	"  --edit-policy   Edit a policy object\n"
++	"  --delete-policy Edit a policy object\n"
++	"  --help          Display this message\n"
++	"\nUse \"--query help\" to get help on e.g. the query action\n"
++	);
++	exit(exval);
++}
++
++int
++parse_registration(char **argv, int argc, isns_object_list_t *objs, isns_object_t *key_obj)
++{
++	struct sockaddr_storage def_addr;
++	isns_object_t	*entity = NULL, *last_portal = NULL, *last_node = NULL;
++	const char	*def_port = NULL;
++	int		i;
++
++	if (argc == 1 && !strcmp(argv[0], "help")) {
++		printf("Object registration:\n"
++		       " isnsadm [-key attr=value] --register type,attr=value,... type,attr=value,...\n"
++		       "Where type can be one of:\n"
++		       "  entity         create/update network entity\n"
++		       "  initiator      create iSCSI initiator storage node\n"
++		       "  target         create iSCSI target storage node\n"
++		       "  control        create control node\n"
++		       "  portal         create portal\n"
++		       "  pg             create portal group\n"
++		       "\nThe following attributes are recognized:\n");
++
++		isns_attr_list_parser_help(NULL);
++		exit(0);
++	}
++
++	if (argc == 0)
++		usage(1, "Missing object list\n");
++
++	if (key_obj) {
++		//isns_object_list_append(objs, key_obj);
++		if (isns_object_is_entity(key_obj))
++			entity = key_obj;
++	}
++
++	def_addr = opt_myaddr;
++
++	for (i = 0; i < argc; ++i) {
++		isns_attr_list_t attrlist = ISNS_ATTR_LIST_INIT;
++		struct isns_attr_list_parser state;
++		isns_object_t	*obj;
++		char		*type, *name, *value, *next_attr;
++		char		*attrs[128];
++		unsigned int	nattrs = 0;
++
++		name = argv[i];
++
++		if ((next_attr = strchr(name, ',')) != NULL)
++			*next_attr++ = '\0';
++
++		while (next_attr && *next_attr) {
++			if (nattrs > 128)
++				isns_fatal("Too many attributes\n");
++
++			/* Show mercy with fat fingered
++			 * people,,,,who,cannot,,,type,properly */
++			if (next_attr[0] != ',')
++				attrs[nattrs++] = next_attr;
++			if ((next_attr = strchr(next_attr, ',')) != NULL)
++				*next_attr++ = '\0';
++		}
++
++		if ((value = strchr(name, '=')) != NULL)
++			*value++ = '\0';
++
++		type = name;
++		if (!strcmp(name, "entity")) {
++			if (entity == NULL) {
++				isns_error("Cannot create entity object "
++					"within this key object\n");
++				return 0;
++			}
++
++			if (value != NULL)
++				isns_object_set_string(entity,
++						ISNS_TAG_ENTITY_IDENTIFIER,
++						value);
++			obj = isns_object_get(entity);
++			goto handle_attributes;
++		} else
++		if (!strcmp(name, "node")
++		 || !strcmp(name, "initiator")) {
++			const char *node_name;
++
++			node_name = isns_config.ic_source_name;
++			if (value)
++				node_name = value;
++
++			obj = isns_create_storage_node(node_name,
++					ISNS_ISCSI_INITIATOR_MASK,
++					entity);
++			last_node = obj;
++
++			isns_addr_set_port((struct sockaddr *) &def_addr,
++					ISNS_DEFAULT_PORT_INITIATOR);
++			def_port = "iscsi";
++		} else
++		if (!strcmp(name, "target")) {
++			const char *node_name;
++
++			node_name = isns_config.ic_source_name;
++			if (value)
++				node_name = value;
++			obj = isns_create_storage_node(node_name,
++					ISNS_ISCSI_TARGET_MASK,
++					entity);
++			last_node = obj;
++
++			isns_addr_set_port((struct sockaddr *) &def_addr,
++					ISNS_DEFAULT_PORT_TARGET);
++			def_port = "iscsi-target";
++		} else
++		if (!strcmp(name, "control")) {
++			const char *node_name;
++
++			node_name = isns_config.ic_control_name;
++			if (value)
++				node_name = value;
++			obj = isns_create_storage_node(node_name,
++					ISNS_ISCSI_CONTROL_MASK,
++					entity);
++			last_node = obj;
++
++			def_port = NULL;
++		} else
++		if (!strcmp(name, "portal")) {
++			isns_portal_info_t portal_info;
++
++			if (value == NULL) {
++				if (def_port == NULL)
++					isns_fatal("portal must follow initiator or target\n");
++				isns_portal_init(&portal_info,
++						(struct sockaddr *) &def_addr,
++						IPPROTO_TCP);
++			} else
++			if (!isns_portal_parse(&portal_info, value, def_port))
++				isns_fatal("Unable to parse portal=%s\n", value);
++			obj = isns_create_portal(&portal_info, entity);
++			last_portal = obj;
++		} else
++		if (!strcmp(name, "pg")) {
++			if (value)
++				isns_fatal("Unexpected value for portal group\n");
++			if (!last_portal || !last_node)
++				isns_fatal("Portal group registration must follow portal and node\n");
++			obj = isns_create_portal_group(last_portal, last_node, 10);
++		} else {
++			isns_error("Unknown object type \"%s\"\n", name);
++			return 0;
++		}
++
++		if (obj == NULL) {
++			isns_error("Failure to create %s object\n", name);
++			return 0;
++		}
++		isns_object_list_append(objs, obj);
++
++handle_attributes:
++		isns_attr_list_parser_init(&state, obj->ie_template);
++		state.default_port = def_port;
++
++		if (!isns_parse_attrs(nattrs, attrs, &attrlist, &state)
++		 || !isns_object_set_attrlist(obj, &attrlist)) {
++			isns_error("Failure to set all %s attributes\n", name);
++			isns_attr_list_destroy(&attrlist);
++			return 0;
++		}
++
++		isns_attr_list_destroy(&attrlist);
++		isns_object_release(obj);
++	}
++
++	return 1;
++}
++
++static int
++__register_objects(isns_client_t *clnt,
++		isns_object_t *key_obj,
++		const isns_object_list_t *objects)
++{
++	isns_source_t	*source = NULL;
++	isns_simple_t	*reg;
++	uint32_t	status;
++	unsigned int	i;
++
++	for (i = 0; i < objects->iol_count && !source; ++i) {
++		isns_object_t *obj = objects->iol_data[i];
++
++		if (!isns_object_is_iscsi_node(obj))
++			continue;
++		source = isns_source_from_object(obj);
++	}
++
++	reg = isns_create_registration2(clnt, key_obj, source);
++	isns_registration_set_replace(reg, opt_replace);
++
++	/* Add all objects to be registered */
++	for (i = 0; i < objects->iol_count; ++i)
++		isns_registration_add_object(reg, objects->iol_data[i]);
++
++	status = isns_client_call(clnt, &reg);
++	isns_simple_free(reg);
++
++	if (status == ISNS_SUCCESS)
++		printf("Successfully registered object(s)\n");
++	else
++		isns_error("Failed to register object(s): %s\n",
++				isns_strerror(status));
++
++	if (source)
++		isns_source_release(source);
++	return status;
++}
++
++int
++register_objects(isns_client_t *clnt,
++		int argc, char **argv)
++{
++	isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
++	isns_object_t	*key_obj = NULL;
++	uint32_t	status;
++
++	if (opt_key != NULL) {
++		isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
++		struct isns_attr_list_parser state;
++
++		isns_attr_list_parser_init(&state, NULL);
++
++		if (!isns_parse_attrs(1, &opt_key, &key_attrs, &state)) {
++			isns_error("Cannot parse registration key \"%s\"\n",
++					opt_key);
++			return 0;
++		}
++
++		key_obj = isns_create_object(isns_attr_list_parser_context(&state),
++				&key_attrs, NULL);
++		isns_attr_list_destroy(&key_attrs);
++
++		if (!key_obj) {
++			isns_error("Cannot create registration key object\n");
++			return 0;
++		}
++	} else {
++		/* If the user does not provide a key object, 
++		 * create/update an entity.
++		 */
++		key_obj = isns_create_entity(ISNS_ENTITY_PROTOCOL_ISCSI, NULL);
++	}
++
++	if (!parse_registration(argv, argc, &objects, key_obj))
++		isns_fatal("Unable to parse registration\n");
++
++	status = __register_objects(clnt, key_obj, &objects);
++	isns_object_list_destroy(&objects);
++
++	isns_object_release(key_obj);
++	return status;
++}
++
++/*
++ * Parse the query string given by the user
++ *
++ * 5.6.5.2
++ * The Message Key may contain key or non-key attributes or no
++ * attributes at all.  If multiple attributes are used as the
++ * Message Key, then they MUST all be from the same object type
++ * (e.g., IP address and TCP/UDP Port are attributes of the
++ * Portal object type).
++ */
++int
++parse_query(char **argv, int argc, isns_attr_list_t *keys, isns_attr_list_t *query)
++{
++	struct isns_attr_list_parser state;
++
++	isns_attr_list_parser_init(&state, NULL);
++	state.nil_permitted = 1;
++
++	if (argc == 1 && !strcmp(argv[0], "help")) {
++		printf("Object query:\n"
++		       " isnsadm --query attr=value attr=value ... ?query-attr ?query-attr ...\n"
++		       "All key attributes must refer to a common object type.\n"
++		       "Query attributes specify the attributes the server should return,"
++		       "and can refer to any object type.\n"
++		       "The following attributes are recognized:\n");
++		isns_attr_list_parser_help(&state);
++		exit(0);
++	}
++
++	if (argc == 0)
++		isns_fatal("Missing query attributes\n");
++
++	return isns_parse_query_attrs(argc, argv, keys, query, &state);
++}
++
++int
++query_objects(isns_client_t *clnt, int argc, char **argv)
++{
++	isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT;
++	isns_attr_list_t oper_attrs = ISNS_ATTR_LIST_INIT;
++	isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
++	uint32_t	status;
++	isns_simple_t	*qry;
++	unsigned int	i;
++
++	if (!parse_query(argv, argc, &query_key, &oper_attrs))
++		isns_fatal("Unable to parse query\n");
++
++	qry = isns_create_query(clnt, &query_key);
++	isns_attr_list_destroy(&query_key);
++
++	/* Add the list of attributes we request */
++	for (i = 0; i < oper_attrs.ial_count; ++i)
++		isns_query_request_attr(qry, oper_attrs.ial_data[i]);
++	isns_attr_list_destroy(&oper_attrs);
++
++	status = isns_client_call(clnt, &qry);
++	if (status != ISNS_SUCCESS) {
++		isns_error("Query failed: %s\n", isns_strerror(status));
++		return status;
++	}
++
++	status = isns_query_response_get_objects(qry, &objects);
++	if (status) {
++		isns_error("Unable to extract object list from query response: %s\n",
++				isns_strerror(status), status);
++		return status;
++	}
++
++	isns_object_list_print(&objects, isns_print_stdout);
++	isns_object_list_destroy(&objects);
++	isns_simple_free(qry);
++
++	return status;
++}
++
++int
++query_entity_id(isns_client_t *clnt, int argc, char **argv)
++{
++	isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT;
++	isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
++	uint32_t	status;
++	isns_simple_t	*qry;
++	const char	*eid;
++
++	if (argc == 1 && !strcmp(argv[0], "help")) {
++		printf("Query iSNS for own entity ID.\n"
++		       "No arguments allowed\n");
++		exit(0);
++	}
++	if (argc != 0)
++		isns_fatal("EID query - no arguments accepted\n");
++
++	isns_attr_list_append_string(&query_key,
++			ISNS_TAG_ISCSI_NAME,
++			isns_config.ic_source_name);
++	qry = isns_create_query(clnt, &query_key);
++	isns_attr_list_destroy(&query_key);
++
++	isns_query_request_attr_tag(qry, ISNS_TAG_ENTITY_IDENTIFIER);
++
++	status = isns_client_call(clnt, &qry);
++	if (status != ISNS_SUCCESS) {
++		isns_error("Query failed: %s\n", isns_strerror(status));
++		return status;
++	}
++
++	status = isns_query_response_get_objects(qry, &objects);
++	if (status) {
++		isns_error("Unable to extract object list from query response: %s\n",
++				isns_strerror(status), status);
++		return status;
++	}
++
++	status = ISNS_NO_SUCH_ENTRY;
++	if (objects.iol_count == 0) {
++		isns_error("Node %s not registered with iSNS\n",
++				isns_config.ic_source_name);
++	} else
++	if (!isns_object_get_string(objects.iol_data[0],
++				ISNS_TAG_ENTITY_IDENTIFIER, &eid)) {
++		isns_error("Query for %s returned an object without EID\n",
++				isns_config.ic_source_name);
++	} else {
++		printf("%s\n", eid);
++		status = ISNS_SUCCESS;
++	}
++
++	isns_object_list_destroy(&objects);
++	isns_simple_free(qry);
++
++	return status;
++}
++
++/*
++ * Parse the list query string given by the user
++ */
++int
++parse_list(int argc, char **argv, isns_object_template_t **type_p, isns_attr_list_t *keys)
++{
++	struct isns_attr_list_parser state;
++	isns_object_template_t *query_type = NULL;
++	char	*type_name;
++
++	if (argc == 0)
++		usage(1, "Missing object type");
++
++	if (argc == 1 && !strcmp(argv[0], "help")) {
++		printf("Object query:\n"
++		       " isnsadm --list type attr=value attr=value ...\n"
++		       "Possible value for <type>:\n"
++		       " entities           - list all network entites\n"
++		       " nodes              - list all storage nodes\n"
++		       " portals            - list all portals\n"
++		       " portal-groups      - list all portal groups\n"
++		       " dds                - list all discovery domains\n"
++		       " ddsets             - list all discovery domains sets\n"
++		       " policies           - list all policies (privileged)\n"
++		       "Additional attributes can be specified to scope the\n"
++		       "search. They must match the specified object type.\n"
++		       "\nThe following attributes are recognized:\n");
++		isns_attr_list_parser_help(NULL);
++		exit(0);
++	}
++
++	type_name = *argv++; --argc;
++	if (!strcasecmp(type_name, "entities"))
++		query_type = &isns_entity_template;
++	else
++	if (!strcasecmp(type_name, "nodes"))
++		query_type = &isns_iscsi_node_template;
++	else
++	if (!strcasecmp(type_name, "portals"))
++		query_type = &isns_portal_template;
++	else
++	if (!strcasecmp(type_name, "portal-groups"))
++		query_type = &isns_iscsi_pg_template;
++	else
++	if (!strcasecmp(type_name, "dds"))
++		query_type = &isns_dd_template;
++	else
++	if (!strcasecmp(type_name, "ddsets"))
++		query_type = &isns_ddset_template;
++	else
++	if (!strcasecmp(type_name, "policies"))
++		query_type = &isns_policy_template;
++	else {
++		isns_error("Unknown object type \"%s\"\n",
++				type_name);
++		return 0;
++	}
++
++	*type_p = query_type;
++
++	isns_attr_list_parser_init(&state, query_type);
++	state.nil_permitted = 1;
++
++	return isns_parse_attrs(argc, argv, keys, &state);
++}
++
++int
++list_objects(isns_client_t *clnt, int argc, char **argv)
++{
++	isns_attr_list_t	query_keys = ISNS_ATTR_LIST_INIT;
++	isns_object_template_t	*query_type = NULL;
++	isns_simple_t		*simp;
++	int			status, count = 0;
++
++	if (!parse_list(argc, argv, &query_type, &query_keys))
++		isns_fatal("Unable to parse parameters\n");
++
++	simp = isns_create_getnext(clnt, query_type, &query_keys);
++	while (1) {
++		isns_object_t	*obj = NULL;
++		isns_simple_t	*followup;
++
++		status = isns_client_call(clnt, &simp);
++		if (status)
++			break;
++
++		status = isns_getnext_response_get_object(simp, &obj);
++		if (status)
++			break;
++
++		printf("Object %u:\n", count++);
++		isns_object_print(obj, isns_print_stdout);
++		isns_object_release(obj);
++
++		followup = isns_create_getnext_followup(clnt,
++				simp, &query_keys);
++		isns_simple_free(simp);
++		simp = followup;
++	}
++
++	if (status == ISNS_SOURCE_UNAUTHORIZED
++	 && query_type == &isns_policy_template
++	 && !opt_local)
++		isns_warning("Please use --local trying to list policies\n");
++
++	if (status != ISNS_NO_SUCH_ENTRY) {
++		isns_error("GetNext call failed: %s\n",
++				isns_strerror(status));
++		return status;
++	}
++	return ISNS_SUCCESS;
++}
++
++/*
++ * Parse the deregistration string given by the user
++ *
++ * 5.6.5.2
++ * The Message Key may contain key or non-key attributes or no
++ * attributes at all.  If multiple attributes are used as the
++ * Message Key, then they MUST all be from the same object type
++ * (e.g., IP address and TCP/UDP Port are attributes of the
++ * Portal object type).
++ */
++int
++parse_deregistration(char **argv, int argc, isns_attr_list_t *keys)
++{
++	struct isns_attr_list_parser state;
++
++	isns_attr_list_parser_init(&state, NULL);
++	state.multi_type_permitted = 1;
++	state.nil_permitted = 1;
++
++	if (argc == 1 && !strcmp(argv[0], "help")) {
++		printf("Object deregistration:\n"
++		       " isnsadm --deregister attr=value attr=value ...\n"
++		       "All attributes must refer to a common object type.\n"
++		       "\nThe following attributes are recognized:\n");
++		isns_attr_list_parser_help(&state);
++		exit(0);
++	}
++
++	return isns_parse_attrs(argc, argv, keys, &state);
++}
++
++int
++deregister_objects(isns_client_t *clnt, int argc, char **argv)
++{
++	isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT;
++	isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
++	isns_simple_t	*dereg;
++	uint32_t	status;
++
++	if (!parse_deregistration(argv, argc, &query_key))
++		isns_fatal("Unable to parse unregistration\n");
++
++	dereg = isns_create_deregistration(clnt, &query_key);
++	isns_attr_list_destroy(&query_key);
++
++	status = isns_client_call(clnt, &dereg);
++	if (status != ISNS_SUCCESS) {
++		isns_error("Deregistration failed: %s\n",
++				isns_strerror(status));
++		return status;
++	}
++
++#if 0
++	status = isns_dereg_msg_response_get_objects(dereg, &objects);
++	if (status) {
++		isns_error("Unable to extract object list from deregistration response: %s\n",
++				isns_strerror(status), status);
++		goto done;
++	}
++	isns_object_list_print(&objects, isns_print_stdout);
++#endif
++
++	isns_object_list_destroy(&objects);
++	isns_simple_free(dereg);
++
++	return status;
++}
++
++/*
++ * Handle discovery domain registration/deregistration
++ */
++int
++parse_dd_registration(char **argv, int argc, isns_attr_list_t *keys)
++{
++	struct isns_attr_list_parser state;
++
++	isns_attr_list_parser_init(&state, &isns_dd_template);
++	if (argc == 1 && !strcmp(argv[0], "help")) {
++		printf("Object query:\n"
++		       " isnsadm --dd-register attr=value attr=value ...\n"
++		       "You cannot specify more than one domain.\n"
++		       "If you want to modify an existing domain, you must specify its ID.\n"
++		       "The following attributes are recognized:\n");
++		isns_attr_list_parser_help(&state);
++		exit(0);
++	}
++
++	return isns_parse_attrs(argc, argv, keys, &state);
++}
++
++int
++register_domain(isns_client_t *clnt, int argc, char **argv)
++{
++	isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
++	isns_simple_t	*msg;
++	uint32_t	status;
++
++	if (!parse_dd_registration(argv, argc, &attrs))
++		isns_fatal("Unable to parse DD registration\n");
++
++	msg = isns_create_dd_registration(clnt, &attrs);
++	isns_attr_list_destroy(&attrs);
++
++	if (msg == NULL) {
++		isns_error("Cannot create message\n");
++		return ISNS_INTERNAL_ERROR;
++	}
++
++	status = isns_client_call(clnt, &msg);
++	if (status != ISNS_SUCCESS) {
++		isns_error("Registration failed: %s\n",
++				isns_strerror(status));
++		return status;
++	}
++
++	if (status == ISNS_SUCCESS) {
++		printf("Registered DD:\n");
++		isns_attr_list_print(
++				isns_simple_get_attrs(msg),
++				isns_print_stdout);
++	}
++	isns_simple_free(msg);
++
++	return status;
++}
++
++int
++parse_dd_deregistration(char **argv, int argc,
++		uint32_t *dd_id, isns_attr_list_t *keys)
++{
++	struct isns_attr_list_parser state;
++
++	isns_attr_list_parser_init(&state, &isns_dd_template);
++	if (argc == 0 || (argc == 1 && !strcmp(argv[0], "help"))) {
++		printf("DD deregistration:\n"
++		       " isnsadm --dd-deregister dd-id attr=value attr=value ...\n"
++		       "You cannot specify more than one domain.\n"
++		       "The following attributes are recognized:\n");
++		isns_attr_list_parser_help(&state);
++		exit(0);
++	}
++
++	*dd_id = parse_count(argv[0]);
++
++	return isns_parse_attrs(argc - 1, argv + 1, keys, &state);
++}
++
++int
++deregister_domain(isns_client_t *clnt, int argc, char **argv)
++{
++	isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
++	isns_simple_t	*msg;
++	uint32_t	dd_id, status;
++
++	if (!parse_dd_deregistration(argv, argc, &dd_id, &attrs))
++		isns_fatal("Unable to parse DD registration\n");
++
++	msg = isns_create_dd_deregistration(clnt, dd_id, &attrs);
++	isns_attr_list_destroy(&attrs);
++
++	if (msg == NULL) {
++		isns_error("Cannot create message\n");
++		return ISNS_INTERNAL_ERROR;
++	}
++
++	status = isns_client_call(clnt, &msg);
++	if (status != ISNS_SUCCESS) {
++		isns_error("Deregistration failed: %s\n",
++				isns_strerror(status));
++		return status;
++	}
++
++	isns_simple_free(msg);
++	return status;
++}
++
++/*
++ * Parse a policy
++ */
++int
++parse_policy(int argc, char **argv, isns_attr_list_t *attrs,
++		const char *help_title, const char *help_action)
++{
++	struct isns_attr_list_parser state;
++
++	isns_attr_list_parser_init(&state, &isns_policy_template);
++	state.nil_permitted = 0;
++	state.load_key = load_key_callback;
++	state.generate_key = generate_key_callback;
++
++	if (argc == 1 && !strcmp(argv[0], "help")) {
++		printf("%s:\n"
++		       " isnsadm %s attr=value attr=value ...\n"
++		       "Specifying a Security Policy Index is mandatory.\n"
++		       "\nThe following attributes are recognized:\n",
++		       help_title, help_action);
++		isns_attr_list_parser_help(&state);
++		exit(0);
++	}
++
++	return isns_parse_attrs(argc, argv, attrs, &state);
++}
++
++static int
++__create_policy(isns_client_t *clnt, const isns_attr_list_t *attrs)
++{
++	isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
++	isns_object_t	*obj;
++	int		status;
++
++	obj = isns_create_object(&isns_policy_template, attrs, NULL);
++	if (!obj)
++		isns_fatal("Cannot create policy object\n");
++	isns_object_list_append(&objects, obj);
++
++	status = __register_objects(clnt, NULL, &objects);
++	isns_object_list_destroy(&objects);
++	return status;
++}
++
++/*
++ * Enroll a new client
++ */
++int
++enroll_client(isns_client_t *clnt, int argc, char **argv)
++{
++	isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
++	const char	*client_name;
++	int		status;
++
++	if (argc == 0)
++		usage(1, "Missing client name");
++
++	client_name = *argv++; --argc;
++
++	isns_attr_list_append_string(&attrs,
++			OPENISNS_TAG_POLICY_SPI,
++			client_name);
++#if 0
++	isns_attr_list_append_string(&attrs,
++			OPENISNS_TAG_POLICY_SOURCE_NAME,
++			client_name);
++#endif
++
++	if (!opt_keyfile) {
++		static char 	namebuf[PATH_MAX];
++
++		snprintf(namebuf, sizeof(namebuf), "%s.key", client_name);
++		opt_keyfile = namebuf;
++	}
++
++	if (argc && !parse_policy(argc, argv, &attrs,
++				"Enroll an iSNS client",
++				"--enroll hostname"))
++		isns_fatal("Cannot parse policy\n");
++
++	/* If no key is given, generate one */
++	if (!isns_attr_list_contains(&attrs, OPENISNS_TAG_POLICY_KEY)) {
++		printf("No key given, generating one\n");
++		isns_attr_list_append_attr(&attrs,
++				generate_key_callback());
++	}
++
++	status = __create_policy(clnt, &attrs);
++	isns_attr_list_destroy(&attrs);
++	return status;
++}
++
++
++/*
++ * Create a new policy
++ */
++int
++edit_policy(isns_client_t *clnt, int argc, char **argv)
++{
++	isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
++	int		status;
++
++	if (!parse_policy(argc, argv, &attrs,
++				"Edit an existing policy",
++				"--edit-policy"))
++		isns_fatal("Cannot parse policy\n");
++
++	status = __create_policy(clnt, &attrs);
++	isns_attr_list_destroy(&attrs);
++
++	return status;
++}
++
++#ifdef WITH_SECURITY
++static isns_attr_t *
++__key_to_attr(EVP_PKEY *pkey)
++{
++	struct __isns_opaque key;
++	isns_value_t	value;
++	isns_attr_t	*attr = NULL;
++
++	if (!isns_dsa_encode_public(pkey, &key.ptr, &key.len))
++		goto out;
++
++	/* Must pad key. This means we may end up encoding a few
++	 * bytes of trash. Oh well. */
++	key.len = ISNS_PAD(key.len);
++
++	value = ISNS_VALUE_INIT(opaque, key);
++	attr = isns_attr_alloc(OPENISNS_TAG_POLICY_KEY, NULL, &value);
++
++	isns_free(key.ptr);
++
++out:
++	EVP_PKEY_free(pkey);
++	return attr;
++}
++
++isns_attr_t *
++generate_key_callback(void)
++{
++	EVP_PKEY	*pkey;
++
++	if (opt_keyfile == NULL)
++		isns_fatal("Key generation requires --keyfile option\n");
++
++	if (!(pkey = isns_dsa_generate_key()))
++		isns_fatal("Key generation failed\n");
++
++	if (!isns_dsa_store_private(opt_keyfile, pkey))
++		isns_fatal("Unable to write private key to %s\n",
++				opt_keyfile);
++
++	printf("Stored DSA private key in %s\n", opt_keyfile);
++	return __key_to_attr(pkey);
++}
++
++isns_attr_t *
++load_key_callback(const char *pathname)
++{
++	EVP_PKEY	*pkey;
++
++	if (!(pkey = isns_dsa_load_public(pathname)))
++		isns_fatal("Unable to load public key from file %s\n", pathname);
++
++	return __key_to_attr(pkey);
++}
++
++#else /* WITH_SECURITY */
++isns_attr_t *
++generate_key_callback(void)
++{
++	isns_fatal("Authentication disabled in this build\n");
++	return NULL;
++}
++
++isns_attr_t *
++load_key_callback(const char *pathname)
++{
++	isns_fatal("Authentication disabled in this build\n");
++	return NULL;
++}
++
++#endif
+diff --git a/utils/open-isns/isnsd.c b/utils/open-isns/isnsd.c
+new file mode 100644
+index 0000000..3f983d6
+--- /dev/null
++++ b/utils/open-isns/isnsd.c
+@@ -0,0 +1,299 @@
++/*
++ * isnsd - the iSNS Daemon
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <getopt.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <signal.h>
++#include <unistd.h>
++
++#ifdef MTRACE
++# include <mcheck.h>
++#endif
++
++#include <isns.h>
++#include "security.h"
++#include "util.h"
++#include "paths.h"
++#include "internal.h"
++
++enum {
++	MODE_NORMAL,
++	MODE_DUMP_DB,
++	MODE_INIT,
++};
++
++static const char *	opt_configfile = ISNS_DEFAULT_ISNSD_CONFIG;
++static int		opt_af = AF_UNSPEC;
++static int		opt_mode = MODE_NORMAL;
++static int		opt_foreground = 0;
++
++static char *		slp_url;
++
++static int		init_server(void);
++static void		run_server(isns_server_t *, isns_db_t *);
++static void		usage(int, const char *);
++static void		cleanup(int);
++
++static struct option	options[] = {
++      {	"config",		required_argument,	NULL,	'c'		},
++      {	"debug",		required_argument,	NULL,	'd'		},
++      { "foreground",		no_argument,		NULL,	'f'		},
++      { "init",			no_argument,		NULL,	MODE_INIT	},
++      { "dump-db",		no_argument,		NULL,	MODE_DUMP_DB	},
++      { "help",			no_argument,		NULL,	'h'		},
++      { "version",		no_argument,		NULL,	'V'		},
++      { NULL }
++};
++
++int
++main(int argc, char **argv)
++{
++	isns_server_t	*server;
++	isns_source_t	*source;
++	isns_db_t	*db;
++	int		c;
++
++#ifdef MTRACE
++	mtrace();
++#endif
++
++	while ((c = getopt_long(argc, argv, "46c:d:fh", options, NULL)) != -1) {
++		switch (c) {
++		case '4':
++			opt_af = AF_INET;
++			break;
++
++		case '6':
++			opt_af = AF_INET6;
++			break;
++
++		case 'c':
++			opt_configfile = optarg;
++			break;
++
++		case 'd':
++			isns_enable_debugging(optarg);
++			break;
++
++		case 'f':
++			opt_foreground = 1;
++			break;
++
++		case MODE_DUMP_DB:
++		case MODE_INIT:
++			opt_mode = c;
++			break;
++
++		case 'h':
++			usage(0, NULL);
++
++		case 'V':
++			printf("Open-iSNS version %s\n"
++			       "Copyright (C) 2007, Olaf Kirch <olaf.kirch@oracle.com>\n",
++			       OPENISNS_VERSION_STRING);
++			return 0;
++
++		default:
++			usage(1, "Unknown option");
++		}
++	}
++
++	if (optind != argc)
++		usage(1, NULL);
++
++	isns_read_config(opt_configfile);
++
++	if (!isns_config.ic_source_name)
++		usage(1, "Please specify an iSNS source name");
++	source = isns_source_create_iscsi(isns_config.ic_source_name);
++
++	if (opt_mode == MODE_INIT)
++		return !init_server();
++
++	if (opt_mode == MODE_NORMAL)
++		isns_write_pidfile(isns_config.ic_pidfile);
++
++	db = isns_db_open(isns_config.ic_database);
++	if (db == NULL)
++		isns_fatal("Unable to open database\n");
++
++	if (opt_mode == MODE_DUMP_DB) {
++		isns_db_print(db, isns_print_stdout);
++		exit(0);
++	}
++
++	if (!opt_foreground) {
++		if (daemon(0, 0) < 0)
++			isns_fatal("Unable to background server process\n");
++		isns_log_background();
++		isns_update_pidfile(isns_config.ic_pidfile);
++	}
++
++	signal(SIGTERM, cleanup);
++	signal(SIGINT, cleanup);
++
++	server = isns_create_server(source, db, &isns_default_service_ops);
++
++	run_server(server, db);
++	return 0;
++}
++
++void
++usage(int exval, const char *msg)
++{
++	if (msg)
++		fprintf(stderr, "Error: %s\n", msg);
++	fprintf(stderr,
++	"Usage: isnsd [options]\n\n"
++	"  --config        Specify alternative config fille\n"
++	"  --foreground    Do not put daemon in the background\n"
++	"  --debug         Enable debugging (list of debug flags)\n"
++	"  --init          Initialize the server (key generation etc)\n"
++	"  --dump-db       Display the database contents and exit\n"
++	"  --help          Print this message\n"
++	);
++	exit(exval);
++}
++
++void
++cleanup(int sig)
++{
++	isns_remove_pidfile(isns_config.ic_pidfile);
++	exit(1);
++}
++
++static void
++slp_cleanup(void)
++{
++	char	*url = slp_url;
++
++	slp_url = NULL;
++	if (url) {
++		isns_slp_unregister(url);
++		isns_free(url);
++	}
++}
++
++/*
++ * Initialize server
++ */
++int
++init_server(void)
++{
++	if (!isns_security_init())
++		return 0;
++
++	/* Anything else? */
++
++	return 1;
++}
++
++/*
++ * Server main loop
++ */
++void
++run_server(isns_server_t *server, isns_db_t *db)
++{
++	isns_socket_t	*sock;
++	isns_security_t	*ctx = NULL;
++	isns_message_t	*msg, *resp;
++	int status;
++
++	if (isns_config.ic_security) {
++		const char	*ksname;
++		isns_keystore_t	*ks;
++
++		ctx = isns_default_security_context(1);
++		if (!(ksname = isns_config.ic_client_keystore))
++			isns_fatal("config problem: no key store specified\n");
++		if (!strcasecmp(ksname, "db:"))
++			ks = isns_create_db_keystore(db);
++		else
++			ks = isns_create_keystore(ksname);
++		if (ks == NULL)
++			isns_fatal("Unable to create keystore %s\n", ksname);
++		isns_security_set_keystore(ctx, ks);
++	}
++
++	status = isns_dd_load_all(db);
++	if (status != ISNS_SUCCESS)
++		isns_fatal("Problem loading Discovery Domains from database\n");
++
++	if (isns_config.ic_control_socket) {
++		sock = isns_create_server_socket(isns_config.ic_control_socket,
++				NULL, AF_UNSPEC, SOCK_STREAM);
++		if (sock == NULL)
++			isns_fatal("Unable to create control socket\n");
++		/*
++		isns_socket_set_security_ctx(sock, ctx);
++		   */
++	}
++
++	sock = isns_create_server_socket(isns_config.ic_bind_address,
++			"isns", opt_af, SOCK_STREAM);
++	if (sock == NULL)
++		isns_fatal("Unable to create server socket\n");
++	isns_socket_set_security_ctx(sock, ctx);
++
++	if (isns_config.ic_slp_register) {
++		slp_url = isns_slp_build_url(0);
++		isns_slp_register(slp_url);
++
++		atexit(slp_cleanup);
++	}
++
++	isns_esi_init(server);
++	isns_scn_init(server);
++
++	while (1) {
++		struct timeval timeout = { 0, 0 };
++		time_t	now, then, next_timeout = time(NULL) + 3600;
++
++		/* Expire entities that haven't seen any activity
++		 * for a while. */
++		if (isns_config.ic_registration_period) {
++			then = isns_db_expire(db);
++			if (then && then < next_timeout)
++				next_timeout = then;
++		}
++
++		/* Run any timers (eg for ESI) */
++		then = isns_run_timers();
++		if (then && then < next_timeout)
++			next_timeout = then;
++
++		/* There may be pending SCNs, push them out now */
++		then = isns_scn_transmit_all();
++		if (then && then < next_timeout)
++			next_timeout = then;
++
++		/* Purge any objects that have been marked for removal
++		 * from the DB (deleting them, or moving them to limbo
++		 * state). */
++		isns_db_purge(db);
++
++		/* Determine how long we can sleep before working
++		 * the ESI queues and DB expiry again. */
++		now = time(NULL);
++		if (next_timeout <= now)
++			continue;
++		timeout.tv_sec = next_timeout - now;
++
++		if ((msg = isns_recv_message(&timeout)) == NULL)
++			continue;
++
++		if ((resp = isns_process_message(server, msg)) != NULL) {
++			isns_socket_t *sock = isns_message_socket(msg);
++
++			isns_socket_send(sock, resp);
++			isns_message_release(resp);
++		}
++
++		isns_message_release(msg);
++	}
++}
+diff --git a/utils/open-isns/isnsdd.c b/utils/open-isns/isnsdd.c
+new file mode 100644
+index 0000000..e4e212d
+--- /dev/null
++++ b/utils/open-isns/isnsdd.c
+@@ -0,0 +1,1153 @@
++/*
++ * isnsdd - the iSNS Discovery Daemon
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ *
++ * The way isnsdd communicates with local services (initiator,
++ * target) is via a set of files and signals. That sounds rather
++ * awkward, but it's a lot simpler to add to these services
++ * than another socket based communication mechanism I guess.
++ */
++
++#include <getopt.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <signal.h>
++#include <unistd.h>
++#include <sys/wait.h>
++
++#ifdef MTRACE
++# include <mcheck.h>
++#endif
++
++#include <isns.h>
++#include "security.h"
++#include "util.h"
++#include "isns-proto.h"
++#include "paths.h"
++#include "attrs.h"
++
++enum {
++	ROLE_INITIATOR = 1,
++	ROLE_MONITOR = 2,
++};
++
++#define ISNSDD_REG_NAME		"isns"
++#define ISNSDD_PGT_OFFSET	10000
++#define MAX_RETRY_TIMEOUT	300
++
++typedef struct isns_proxy isns_proxy_t;
++struct isns_proxy {
++	isns_list_t		ip_list;
++	char *			ip_eid;
++	isns_object_t *		ip_entity;
++	isns_client_t *		ip_client;
++	isns_object_list_t	ip_objects;
++	time_t			ip_last_registration;
++};
++
++static const char *	opt_configfile = ISNS_DEFAULT_ISNSDD_CONFIG;
++static int		opt_af = AF_INET6;
++static int		opt_foreground = 0;
++static int		opt_role = ROLE_INITIATOR;
++static int		opt_scn_bits = ISNS_SCN_OBJECT_UPDATED_MASK |
++				ISNS_SCN_OBJECT_ADDED_MASK |
++				ISNS_SCN_OBJECT_REMOVED_MASK |
++				ISNS_SCN_TARGET_AND_SELF_ONLY_MASK;
++static unsigned int	opt_retry_timeout = 10;
++static int		opt_esi = 1;
++
++static isns_socket_t *	server_socket;
++static ISNS_LIST_DECLARE(proxies);
++static isns_object_list_t local_registry = ISNS_OBJECT_LIST_INIT;
++static isns_object_list_t local_portals = ISNS_OBJECT_LIST_INIT;
++static isns_object_list_t visible_nodes = ISNS_OBJECT_LIST_INIT;
++static unsigned int	esi_interval;
++static int		should_reexport;
++
++static void		run_discovery(isns_server_t *srv);
++static void		scn_callback(isns_db_t *, uint32_t,
++				isns_object_template_t *,
++				const char *, const char *);
++static void		refresh_registration(void *);
++static void		retry_registration(void *);
++static void		load_exported_objects(void);
++static void		store_imported_objects(void);
++static void		usage(int, const char *);
++
++static void		install_sighandler(int, void (*func)(int));
++static void		sig_cleanup(int);
++static void		sig_reread(int);
++
++static struct option	options[] = {
++      {	"config",		required_argument,	NULL,	'c'		},
++      {	"debug",		required_argument,	NULL,	'd'		},
++      { "foreground",		no_argument,		NULL,	'f'		},
++      { "role",			required_argument,	NULL,	'r'		},
++      { "no-esi",		no_argument,		NULL,	'E'		},
++      { "help",			no_argument,		NULL,	'h'		},
++      { "version",		no_argument,		NULL,	'V'		},
++      { NULL }
++};
++
++int
++main(int argc, char **argv)
++{
++	isns_server_t	*server;
++	isns_source_t	*source;
++	isns_db_t	*db;
++	int		c;
++
++#ifdef MTRACE
++	mtrace();
++#endif
++
++	while ((c = getopt_long(argc, argv, "46c:d:Efhr:", options, NULL)) != -1) {
++		switch (c) {
++		case '4':
++			opt_af = AF_INET;
++			break;
++
++		case '6':
++			opt_af = AF_INET6;
++			break;
++
++		case 'c':
++			opt_configfile = optarg;
++			break;
++
++		case 'd':
++			isns_enable_debugging(optarg);
++			break;
++
++		case 'E':
++			opt_esi = 0;
++			break;
++
++		case 'f':
++			opt_foreground = 1;
++			break;
++
++		case 'h':
++			usage(0, NULL);
++
++		case 'r':
++			if (!strcasecmp(optarg, "initiator"))
++				opt_role = ROLE_INITIATOR;
++			else
++			if (!strcasecmp(optarg, "control")
++			 || !strcasecmp(optarg, "monitor"))
++				opt_role = ROLE_MONITOR;
++			else {
++				isns_error("Unknown role \"%s\"\n", optarg);
++				usage(1, NULL);
++			}
++			break;
++
++		case 'V':
++			printf("Open-iSNS version %s\n"
++			       "Copyright (C) 2007, Olaf Kirch <olaf.kirch@oracle.com>\n",
++			       OPENISNS_VERSION_STRING);
++			return 0;
++
++		default:
++			usage(1, "Unknown option");
++		}
++	}
++
++	if (optind != argc)
++		usage(1, NULL);
++
++	/* If the config code derives the source name
++	 * automatically, we want it to be distinct from
++	 * any other source name (chosen by eg the iSCSI
++	 * initiator). Adding a suffix of ":isns" is a
++	 * somewhat lame attempt.
++	 */
++	isns_config.ic_source_suffix = "isns";
++
++	isns_read_config(opt_configfile);
++
++	if (!isns_config.ic_source_name)
++		usage(1, "Please specify an iSNS source name");
++	source = isns_source_create_iscsi(isns_config.ic_source_name);
++
++	isns_write_pidfile(isns_config.ic_pidfile);
++
++	if (!opt_foreground) {
++		if (daemon(0, 0) < 0)
++			isns_fatal("Unable to background server process\n");
++		isns_log_background();
++		isns_update_pidfile(isns_config.ic_pidfile);
++	}
++
++	install_sighandler(SIGTERM, sig_cleanup);
++	install_sighandler(SIGINT, sig_cleanup);
++	install_sighandler(SIGUSR2, sig_reread);
++
++	/* Create a DB object that shadows our portal list. This is for ESI -
++	 * when an ESI comes in, the library will look up the portal in this
++	 * database, and update its mtime. By checking the mtime at regular
++	 * intervals, we can verify whether the server's ESIs actually
++	 * reach us.
++	 */
++	db = isns_db_open_shadow(&local_portals);
++
++	server = isns_create_server(source, db, &isns_callback_service_ops);
++	isns_server_set_scn_callback(server, scn_callback);
++
++	run_discovery(server);
++	return 0;
++}
++
++void
++usage(int exval, const char *msg)
++{
++	if (msg)
++		fprintf(stderr, "Error: %s\n", msg);
++	fprintf(stderr,
++	"Usage: isnsdd [options]\n\n"
++	"  --role <role>   Specify role (one of initiator, control)\n"
++	"  --config        Specify alternative config fille\n"
++	"  --foreground    Do not put daemon in the background\n"
++	"  --no-esi        Do not try to register an portals for ESI status inquiries\n"
++	"  --debug         Enable debugging (list of debug flags)\n"
++	"  --help          Print this message\n"
++	);
++	exit(exval);
++}
++
++void
++install_sighandler(int signo, void (*func)(int))
++{
++	struct sigaction act;
++
++	memset(&act, 0, sizeof(act));
++	act.sa_handler = func;
++	sigaction(signo, &act, NULL);
++}
++
++void
++sig_reread(int sig)
++{
++	should_reexport = 1;
++}
++
++void
++sig_cleanup(int sig)
++{
++	isns_remove_pidfile(isns_config.ic_pidfile);
++	exit(1);
++}
++
++/*
++ * Proxy handling functions
++ */
++static isns_proxy_t *
++isns_create_proxy(const char *eid)
++{
++	isns_proxy_t *proxy;
++
++	proxy = calloc(1, sizeof(*proxy));
++	isns_list_init(&proxy->ip_list);
++	proxy->ip_eid = strdup(eid);
++	return proxy;
++}
++
++static isns_proxy_t *
++__isns_proxy_find(isns_list_t *head, const char *eid)
++{
++	isns_list_t	*pos, *next;
++
++	isns_list_foreach(head, pos, next) {
++		isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, pos);
++
++		if (!strcmp(proxy->ip_eid, eid))
++			return proxy;
++	}
++	return NULL;
++}
++
++static isns_proxy_t *
++isns_proxy_by_entity(const isns_object_t *entity)
++{
++	isns_list_t	*pos, *next;
++
++	isns_list_foreach(&proxies, pos, next) {
++		isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, pos);
++
++		if (proxy->ip_entity == entity)
++			return proxy;
++	}
++	return NULL;
++}
++
++static void
++isns_proxy_erase(isns_proxy_t *proxy)
++{
++	isns_object_list_destroy(&proxy->ip_objects);
++	if (proxy->ip_client) {
++		isns_client_destroy(proxy->ip_client);
++		proxy->ip_client = NULL;
++	}
++	if (proxy->ip_entity) {
++		isns_object_release(proxy->ip_entity);
++		proxy->ip_entity = NULL;
++	}
++	isns_cancel_timer(refresh_registration, proxy);
++}
++
++static void
++isns_proxy_free(isns_proxy_t *proxy)
++{
++	isns_proxy_erase(proxy);
++	isns_list_del(&proxy->ip_list);
++	free(&proxy->ip_eid);
++	free(proxy);
++}
++
++/*
++ * Force a re-registration of the whole object set.
++ */
++static void
++force_reregistration(isns_proxy_t *proxy)
++{
++	isns_cancel_timer(refresh_registration, proxy);
++	isns_add_oneshot_timer(0, retry_registration, proxy);
++}
++
++/*
++ * Refresh the registration by calling DevAttrQry
++ */
++static void
++refresh_registration(void *ptr)
++{
++	isns_proxy_t	*proxy = ptr;
++	isns_client_t	*clnt = proxy->ip_client;
++	isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
++	isns_attr_list_t query_key = ISNS_ATTR_LIST_INIT;
++	isns_simple_t	*qry = NULL;
++	uint32_t	status;
++
++	isns_debug_state("Refreshing registration for %s\n", proxy->ip_eid);
++	isns_attr_list_append_string(&query_key,
++			ISNS_TAG_ENTITY_IDENTIFIER,
++			proxy->ip_eid);
++
++	qry = isns_create_query(clnt, &query_key);
++	isns_attr_list_destroy(&query_key);
++
++	/* We should have an async call mechanism. If the server
++	 * is wedged, we'll block here, unable to service any other
++	 * functions.
++	 */
++	status = isns_simple_call(clnt->ic_socket, &qry);
++	if (status != ISNS_SUCCESS) {
++		isns_error("Query failed: %s\n", isns_strerror(status));
++		goto re_register;
++	}
++
++	status = isns_query_response_get_objects(qry, &objects);
++	isns_simple_free(qry);
++
++	if (status == ISNS_SUCCESS) {
++		if (objects.iol_count != 0)
++			return;
++	} else {
++		isns_error("Unable to parse query response\n");
++	}
++
++re_register:
++	isns_warning("Lost registration, trying to re-register\n");
++	force_reregistration(proxy);
++}
++
++/*
++ * Check if all portals have seen ESI messages from the server
++ */
++static void
++check_portal_registration(void *ptr)
++{
++	isns_object_list_t bad_portals = ISNS_OBJECT_LIST_INIT;
++	unsigned int	i, need_reregister = 0, good_portals = 0;
++	time_t		now;
++
++	isns_debug_state("%s()\n", __FUNCTION__);
++	now = time(NULL);
++	for (i = 0; i < local_portals.iol_count; ++i) {
++		isns_object_t *obj = local_portals.iol_data[i];
++		isns_portal_info_t portal_info;
++		isns_proxy_t	*proxy;
++		time_t		last_modified;
++		uint32_t	interval;
++
++		if (!isns_object_get_uint32(obj, ISNS_TAG_ESI_INTERVAL, &interval))
++			continue;
++
++		last_modified = isns_object_last_modified(obj);
++		if (last_modified + 2 * interval > now) {
++			good_portals++;
++			continue;
++		}
++
++		isns_portal_from_object(&portal_info,
++				ISNS_TAG_PORTAL_IP_ADDRESS,
++				ISNS_TAG_PORTAL_TCP_UDP_PORT,
++				obj);
++
++		isns_notice("Portal %s did not receive ESIs within %u seconds - "
++			"server may have lost us.\n",
++			isns_portal_string(&portal_info),
++			now - last_modified);
++
++		proxy = isns_proxy_by_entity(isns_object_get_entity(obj));
++		if (!proxy)
++			continue;
++
++		/* If we haven't received ANY ESIs, ever, the portal
++		 * may be using a non-routable IP */
++		if (last_modified <= proxy->ip_last_registration)
++			isns_object_list_append(&bad_portals, obj);
++
++		force_reregistration(proxy);
++		need_reregister++;
++	}
++
++	for (i = 0; i < bad_portals.iol_count; ++i)
++		isns_object_list_remove(&local_portals, bad_portals.iol_data[i]);
++	isns_object_list_destroy(&bad_portals);
++
++	if (need_reregister && local_portals.iol_count == 0) {
++		/* Force a re-registration from scratch.
++		 * This time without ESI.
++		 */
++		isns_notice("Suspiciously little ESI traffic - server may be broken\n");
++		isns_notice("Disabling ESI\n");
++		opt_esi = 0;
++	}
++}
++
++static void
++setup_esi_watchdog(void)
++{
++	unsigned int	i;
++
++	isns_cancel_timer(check_portal_registration, NULL);
++	esi_interval = 0;
++
++	for (i = 0; i < local_portals.iol_count; ++i) {
++		isns_object_t	*obj = local_portals.iol_data[i];
++		uint32_t	interval;
++
++		/* should always succeed */
++		if (isns_object_get_uint32(obj, ISNS_TAG_ESI_INTERVAL, &interval))
++			continue;
++
++		if (!esi_interval || interval < esi_interval)
++			esi_interval = interval;
++	}
++
++	if (esi_interval) {
++		isns_debug_state("Setting up timer to check for ESI reachability\n");
++		isns_add_timer(esi_interval * 4 / 5,
++				check_portal_registration,
++				NULL);
++	}
++}
++
++static void
++load_exported_objects(void)
++{
++	isns_debug_state("Reading list of exported objects\n");
++	isns_object_list_destroy(&local_registry);
++	if (!isns_local_registry_load("!" ISNSDD_REG_NAME, 0, &local_registry)) {
++		isns_warning("Unable to obtain locally registered objects\n");
++		return;
++	}
++}
++
++static void
++store_imported_objects(void)
++{
++	if (!isns_local_registry_store(ISNSDD_REG_NAME, 0, &visible_nodes))
++		isns_warning("Unable to store discovered objects\n");
++}
++
++/*
++ * Given the DevAttrReg response, extract the entity ID we
++ * have been assigned.
++ */
++static int
++extract_entity_id(isns_proxy_t *proxy, isns_simple_t *resp)
++{
++	isns_object_list_t resp_objects = ISNS_OBJECT_LIST_INIT;
++	isns_object_t	*entity = NULL;
++	int		status;
++	unsigned int	i;
++
++	status = isns_query_response_get_objects(resp, &resp_objects);
++	if (status) {
++		isns_error("Unable to extract object list from "
++			   "registration response: %s\n",
++			   isns_strerror(status), status);
++		goto out;
++	}
++
++	for (i = 0; i < resp_objects.iol_count; ++i) {
++		isns_object_t	*obj = resp_objects.iol_data[i];
++		uint32_t	interval;
++
++		if (!isns_object_is_entity(obj))
++			continue;
++
++		if (entity) {
++			isns_error("Server returns more than one entity "
++				   "in registration response. What a weirdo.\n");
++			continue;
++		}
++		entity = obj;
++
++		if (!isns_object_get_uint32(obj,
++					ISNS_TAG_REGISTRATION_PERIOD,
++					&interval))
++			continue;
++
++		if (interval == 0) {
++			isns_error("Server returns a registration period of 0\n");
++			continue;
++		}
++
++		isns_debug_state("Setting up timer for registration refresh\n");
++		isns_add_timer(interval / 2,
++				refresh_registration,
++				proxy);
++	}
++
++	for (i = 0; i < resp_objects.iol_count; ++i) {
++		isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
++		isns_object_t	*obj = resp_objects.iol_data[i];
++		uint32_t	interval;
++
++		if (!isns_object_is_portal(obj)
++		 || !isns_object_get_uint32(obj, ISNS_TAG_ESI_INTERVAL, &interval))
++			continue;
++		 
++		if (interval == 0) {
++			isns_error("Server returns an ESI interval of 0\n");
++			continue;
++		}
++
++		isns_object_get_key_attrs(obj, &key_attrs);
++		if (!(obj = isns_object_list_lookup(&proxy->ip_objects, NULL, &key_attrs))) {
++			isns_error("Server response includes a portal we never registered\n");
++			continue;
++		}
++
++		isns_object_set_uint32(obj, ISNS_TAG_ESI_INTERVAL, interval);
++
++		/* Server enabled ESI for this portal, so add it to
++		 * the list of local portals we regularly check for
++		 * incoming ESI messages. */
++		isns_object_list_append(&local_portals, obj);
++	}
++
++	proxy->ip_last_registration = time(NULL);
++out:
++	isns_object_list_destroy(&resp_objects);
++	return status;
++}
++
++static inline void
++__add_release_object(isns_object_list_t *objects, isns_object_t *cur)
++{
++	if (cur == NULL)
++		return;
++	isns_object_list_append(objects, cur);
++	isns_object_release(cur);
++}
++
++/*
++ * Rebuild the list of proxies given the set of entities
++ */
++void
++rebuild_proxy_list(isns_object_list_t *entities, isns_list_t *old_list)
++{
++	isns_proxy_t	*proxy;
++	unsigned int	i;
++
++	isns_list_move(old_list, &proxies);
++
++	for (i = 0; i < entities->iol_count; ++i) {
++		isns_object_t	*entity = entities->iol_data[i];
++		isns_object_t	*node;
++		const char	*eid;
++
++		eid = isns_entity_name(entity);
++		if (eid == NULL) {
++			isns_error("Whoopee, entity without name\n");
++			continue;
++		}
++
++		proxy = __isns_proxy_find(old_list, eid);
++		if (proxy == NULL) {
++			proxy = isns_create_proxy(eid);
++		} else {
++			isns_proxy_erase(proxy);
++		}
++
++		isns_object_list_append(&proxy->ip_objects, entity);
++		isns_object_get_descendants(entity, NULL, &proxy->ip_objects);
++
++		node = isns_object_list_lookup(&proxy->ip_objects,
++				&isns_iscsi_node_template,
++				NULL);
++		if (node == NULL) {
++			isns_warning("Service %s did not register any "
++				     "storage nodes - skipped\n", eid);
++			continue;
++		}
++
++		proxy->ip_client = isns_create_client(NULL,
++				isns_storage_node_name(node));
++		proxy->ip_entity = isns_object_get(entity);
++
++		isns_list_del(&proxy->ip_list);
++		isns_list_append(&proxies, &proxy->ip_list);
++	}
++}
++
++/*
++ * Unregister old proxies
++ */
++static void
++unregister_entities(isns_list_t *list)
++{
++	while (!isns_list_empty(list)) {
++		isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, list->next);
++
++		/* XXX send a DevDereg */
++		isns_proxy_free(proxy);
++	}
++}
++
++/*
++ * The local registry creates fake entities to group objects
++ * registered by the same service. We use this to perform
++ * several registration calls, each with a different EID
++ */
++static int
++register_entity(isns_proxy_t *proxy)
++{
++	isns_client_t	*clnt = proxy->ip_client;
++	isns_simple_t	*call = NULL;
++	int		status;
++
++	call = isns_create_registration(clnt, proxy->ip_entity);
++	isns_registration_set_replace(call, 1);
++	isns_registration_add_object_list(call, &proxy->ip_objects);
++
++	status = isns_simple_call(clnt->ic_socket, &call);
++	if (status == ISNS_SUCCESS) {
++		/* Extract the EID and registration period */
++		extract_entity_id(proxy, call);
++	}
++
++	isns_simple_free(call);
++	return status;
++}
++
++static int
++register_exported_entities(void)
++{
++	int		status = ISNS_SUCCESS;
++	isns_list_t	*pos, *next;
++
++	isns_list_foreach(&proxies, pos, next) {
++		isns_proxy_t *proxy = isns_list_item(isns_proxy_t, ip_list, pos);
++
++		status = register_entity(proxy);
++		if (status != ISNS_SUCCESS)
++			break;
++	}
++
++	setup_esi_watchdog();
++	return status;
++}
++
++static void
++all_objects_set(isns_object_list_t *list, uint32_t tag, uint32_t value)
++{
++	unsigned int	i;
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t *obj = list->iol_data[i];
++
++		isns_object_set_uint32(obj, tag, value);
++	}
++}
++
++static void
++all_objects_unset(isns_object_list_t *list, uint32_t tag)
++{
++	unsigned int	i;
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t *obj = list->iol_data[i];
++
++		isns_object_delete_attr(obj, tag);
++	}
++}
++
++static int
++register_exported_objects(isns_client_t *clnt)
++{
++	isns_portal_info_t portal_info;
++	isns_object_list_t entities = ISNS_OBJECT_LIST_INIT;
++	isns_object_list_t portals = ISNS_OBJECT_LIST_INIT;
++	isns_simple_t	*call = NULL;
++	int		status, with_esi;
++	unsigned int	i, my_port;
++	isns_list_t	old_proxies;
++
++	if (!isns_socket_get_portal_info(server_socket, &portal_info))
++		isns_fatal("Unable to get portal info\n");
++	my_port = isns_portal_tcpudp_port(&portal_info);
++
++	/* Look up all entites and portals */
++	isns_object_list_gang_lookup(&local_registry,
++			&isns_entity_template, NULL,
++			&entities);
++	isns_object_list_gang_lookup(&local_registry,
++			&isns_portal_template, NULL,
++			&portals);
++
++	isns_list_init(&old_proxies);
++	rebuild_proxy_list(&entities, &old_proxies);
++	unregister_entities(&old_proxies);
++
++	/* Enable SCN on all portals we're about to register */
++	all_objects_set(&portals, ISNS_TAG_SCN_PORT, my_port);
++
++	/* Try ESI first. If the server doesn't support it, or doesn't
++	 * have the resources to serve us, fall back to normal
++	 * registration refresh. */
++	if (opt_esi) {
++		all_objects_set(&portals,
++				ISNS_TAG_ESI_INTERVAL,
++				isns_config.ic_esi_min_interval);
++		all_objects_set(&portals,
++				ISNS_TAG_ESI_PORT,
++				my_port);
++	}
++
++	for (with_esi = opt_esi; 1; with_esi--) {
++		status = register_exported_entities();
++
++		/* At some point, we need to add these portals
++		 * to the local_portals list so that ESI works
++		 * properly.
++		 * Right now, we extract the portals from the response
++		 * and add those. The down side of this is that we no
++		 * longer use the same object (pointer) to refer to the
++		 * same thing. The up side is that the information returned
++		 * by the server reflects the correct ESI interval.
++		 */
++		if (status == ISNS_SUCCESS)
++			break;
++
++		if (status != ISNS_ESI_NOT_AVAILABLE || with_esi == 0) {
++			isns_error("Failed to register object(s): %s\n",
++					isns_strerror(status));
++			goto out;
++		}
++
++		/* Continue and retry without ESI */
++		all_objects_unset(&portals, ISNS_TAG_ESI_INTERVAL);
++		all_objects_unset(&portals, ISNS_TAG_ESI_PORT);
++	}
++
++	for (i = 0; i < local_registry.iol_count; ++i) {
++		isns_object_t *obj = local_registry.iol_data[i];
++		isns_source_t *source;
++		int	status;
++
++		if (!isns_object_is_iscsi_node(obj)
++		 && !isns_object_is_fc_port(obj))
++			continue;
++
++		if (!(source = isns_source_from_object(obj)))
++			continue;
++		call = isns_create_scn_registration2(clnt, opt_scn_bits, source);
++		status = isns_simple_call(clnt->ic_socket, &call);
++		if (status != ISNS_SUCCESS) {
++			isns_error("SCN registration for %s failed: %s\n",
++					isns_storage_node_name(obj),
++					isns_strerror(status));
++		}
++		isns_source_release(source);
++	}
++
++out:
++	if (call)
++		isns_simple_free(call);
++	isns_object_list_destroy(&entities);
++	isns_object_list_destroy(&portals);
++	return status;
++}
++
++static void
++retry_registration(void *ptr)
++{
++	isns_proxy_t *proxy = ptr;
++	static unsigned int timeout = 0;
++	int	status;
++
++	status = register_exported_objects(proxy->ip_client);
++	if (status) {
++		if (timeout == 0)
++			timeout = opt_retry_timeout;
++		else if (timeout >= MAX_RETRY_TIMEOUT)
++			timeout = MAX_RETRY_TIMEOUT;
++
++		isns_debug_state("Retrying to register in %u seconds\n", timeout);
++		isns_add_oneshot_timer(timeout, retry_registration, proxy);
++
++		/* Exponential backoff */
++		timeout <<= 1;
++	}
++}
++
++/*
++ * Get a list of all visible storage nodes
++ */
++static int
++get_objects_from_query(isns_simple_t *resp)
++{
++	isns_object_list_t resp_objects = ISNS_OBJECT_LIST_INIT;
++	unsigned int	i;
++	int		status;
++
++	status = isns_query_response_get_objects(resp, &resp_objects);
++	if (status) {
++		isns_error("Unable to extract object list from "
++			   "query response: %s\n",
++			   isns_strerror(status));
++		return status;
++	}
++
++	isns_debug_state("Initial query returned %u object(s)\n", resp_objects.iol_count);
++	for (i = 0; i < resp_objects.iol_count; ++i) {
++		isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
++		isns_object_t	*obj = resp_objects.iol_data[i];
++		isns_object_t	*found;
++
++		if (!isns_object_extract_keys(obj, &key_attrs))
++			continue;
++
++		/* Don't add an object twice, and don't add objects
++		 * that *we* registered.
++		 * This still leaves any default PGs created by the server,
++		 * but we cannot help that (for now).
++		 */
++		found = isns_object_list_lookup(&visible_nodes, NULL, &key_attrs);
++		if (!found)
++			found = isns_object_list_lookup(&local_registry, NULL, &key_attrs);
++		if (found) {
++			isns_object_release(found);
++		} else {
++			isns_object_list_append(&visible_nodes, obj);
++		}
++		isns_attr_list_destroy(&key_attrs);
++	}
++
++	isns_object_list_destroy(&resp_objects);
++	return status;
++}
++
++static int
++query_storage_node(isns_source_t *source, const char *name)
++{
++	isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
++	isns_simple_t	*call;
++	uint32_t	tag;
++	int		status;
++	isns_client_t	*clnt;
++
++	if (isns_source_type(source) != ISNS_TAG_ISCSI_NAME) {
++		isns_error("FC source node - doesn't work yet\n");
++		return ISNS_SUCCESS;
++	}
++	clnt = isns_create_client(NULL, isns_source_name(source));
++
++	tag = isns_source_type(source);
++	if (name) {
++		isns_attr_list_append_string(&key_attrs, tag, name);
++	} else {
++		/* Query for visible nodes */
++		isns_attr_list_append_nil(&key_attrs, tag);
++	}
++
++	call = isns_create_query2(clnt, &key_attrs, source);
++	isns_attr_list_destroy(&key_attrs);
++
++	isns_query_request_attr_tag(call, tag);
++	switch (tag) {
++	case ISNS_TAG_ISCSI_NAME:
++		isns_query_request_attr_tag(call, ISNS_TAG_ISCSI_NODE_TYPE);
++		isns_query_request_attr_tag(call, ISNS_TAG_ISCSI_ALIAS);
++		isns_query_request_attr_tag(call, ISNS_TAG_ISCSI_NODE_INDEX);
++
++		isns_query_request_attr_tag(call, ISNS_TAG_PORTAL_IP_ADDRESS);
++		isns_query_request_attr_tag(call, ISNS_TAG_PORTAL_TCP_UDP_PORT);
++		isns_query_request_attr_tag(call, ISNS_TAG_PORTAL_INDEX);
++
++		isns_query_request_attr_tag(call, ISNS_TAG_PG_ISCSI_NAME);
++		isns_query_request_attr_tag(call, ISNS_TAG_PG_PORTAL_IP_ADDR);
++		isns_query_request_attr_tag(call, ISNS_TAG_PG_PORTAL_TCP_UDP_PORT);
++		isns_query_request_attr_tag(call, ISNS_TAG_PG_TAG);
++		isns_query_request_attr_tag(call, ISNS_TAG_PG_INDEX);
++		break;
++
++	default: ;
++	}
++
++	status = isns_simple_call(clnt->ic_socket, &call);
++	if (status == ISNS_SUCCESS)
++		status = get_objects_from_query(call);
++
++	isns_simple_free(call);
++	isns_client_destroy(clnt);
++	return status;
++}
++
++/*
++ * Query for visible iscsi nodes
++ */
++static int
++query_visible(void)
++{
++	unsigned int i;
++
++	for (i = 0; i < local_registry.iol_count; ++i) {
++		isns_object_t	*obj = local_registry.iol_data[i];
++		isns_source_t	*source;
++		int		status;
++
++		if (!isns_object_is_iscsi_node(obj)
++		 && !isns_object_is_fc_port(obj))
++			continue;
++
++		if (isns_object_is_fc_port(obj)) {
++			isns_error("FC source node - sorry, won't work yet\n");
++			continue;
++		}
++
++		if (!(source = isns_source_from_object(obj)))
++			continue;
++		status = query_storage_node(source, NULL);
++		if (status != ISNS_SUCCESS) {
++			isns_warning("Unable to run query on behalf of %s: %s\n",
++					isns_storage_node_name(obj),
++					isns_strerror(status));
++		}
++		isns_source_release(source);
++	}
++	return ISNS_SUCCESS;
++}
++
++/*
++ * Invoke the registered callout program
++ */
++static void
++callout(const char *how, isns_object_t *obj, unsigned int bitmap)
++{
++	char	*argv[128];
++	int	fargc, argc = 0;
++	pid_t	pid;
++
++	if (!isns_config.ic_scn_callout)
++		return;
++
++	argv[argc++] = isns_config.ic_scn_callout;
++	argv[argc++] = (char *) how;
++	fargc = argc;
++
++	argc += isns_print_attrs(obj, argv + argc, 128 - argc);
++
++	pid = fork();
++	if (pid == 0) {
++		execv(argv[0], argv);
++		isns_fatal("Cannot execute %s: %m\n", argv[0]);
++	}
++
++	while (fargc < argc)
++		isns_free(argv[fargc++]);
++
++	if (pid < 0) {
++		isns_error("fork: %m\n");
++		return;
++	}
++
++	while (waitpid(pid, NULL, 0) < 0)
++		;
++}
++
++/*
++ * This is called when we receive a State Change Notification
++ */
++static void
++scn_callback(isns_db_t *db, uint32_t bitmap,
++		isns_object_template_t *node_type,
++		const char *node_name,
++		const char *dst_name)
++{
++	isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
++	uint32_t	key_tag;
++	isns_object_t	*node = NULL, *recipient = NULL;
++
++	isns_notice("%s \"%s\" %s\n",
++			isns_object_template_name(node_type),
++			node_name, isns_event_string(bitmap));
++
++	/* This is either an iSCSI node or a FC node - in
++	   both cases the storage node name is the key attr */
++	if (node_type == &isns_iscsi_node_template) {
++		key_tag = ISNS_TAG_ISCSI_NAME;
++	} else if (node_type == &isns_fc_node_template) {
++		key_tag = ISNS_TAG_FC_PORT_NAME_WWPN;
++	} else
++		return;
++
++	isns_attr_list_append_string(&key_attrs, key_tag, dst_name);
++	recipient = isns_object_list_lookup(&local_registry, node_type, &key_attrs);
++	if (recipient == NULL) {
++		isns_error("Received SCN for unknown recipient \"%s\"\n",
++				dst_name);
++		goto out;
++	}
++	isns_attr_list_destroy(&key_attrs);
++
++	isns_attr_list_append_string(&key_attrs, key_tag, node_name);
++	node = isns_object_list_lookup(&visible_nodes, node_type, &key_attrs);
++
++	if (bitmap & (ISNS_SCN_OBJECT_REMOVED_MASK|ISNS_SCN_DD_MEMBER_REMOVED_MASK)) {
++		if (node) {
++			isns_object_list_remove(&visible_nodes, node);
++			/* FIXME: We also want to remove any PGs associated with
++			 * this node. */
++		}
++		store_imported_objects();
++		callout("remove", node, bitmap);
++	} else
++	if (bitmap & (ISNS_SCN_OBJECT_ADDED_MASK|ISNS_SCN_OBJECT_UPDATED_MASK|ISNS_SCN_DD_MEMBER_ADDED_MASK)) {
++		const char	*how = "add";
++		isns_source_t	*source;
++
++		if (bitmap & ISNS_SCN_OBJECT_UPDATED_MASK)
++			how = "update";
++		if (!node) {
++			node = isns_create_object(node_type, &key_attrs, NULL);
++			if (!node)
++				goto out;
++			isns_object_list_append(&visible_nodes, node);
++		}
++
++		/* Query the server for information on this node */
++		source = isns_source_from_object(recipient);
++		query_storage_node(source, node_name);
++		isns_source_release(source);
++
++		store_imported_objects();
++		callout(how, node, bitmap);
++
++	}
++
++out:
++	if (node)
++		isns_object_release(node);
++	if (recipient)
++		isns_object_release(recipient);
++	isns_attr_list_destroy(&key_attrs);
++}
++
++/*
++ * Server main loop
++ */
++void
++run_discovery(isns_server_t *server)
++{
++	isns_client_t	*clnt;
++	isns_security_t	*ctx = NULL;
++	isns_message_t	*msg, *resp;
++
++	/* Create the server socket */
++	ctx = isns_default_security_context(0);
++	server_socket = isns_create_server_socket(isns_config.ic_bind_address,
++			NULL, opt_af, SOCK_DGRAM);
++	if (server_socket == NULL)
++		isns_fatal("Unable to create server socket\n");
++	isns_socket_set_security_ctx(server_socket, ctx);
++
++	/* Create the client socket */
++	clnt = isns_create_default_client(NULL);
++	if (clnt == NULL)
++		isns_fatal("Cannot connect to server\n");
++
++	/* Load all objects registered by local services */
++	should_reexport = 1;
++
++	while (1) {
++		struct timeval timeout = { 0, 0 };
++		time_t	now, then, next_timeout;
++		unsigned int function;
++
++		next_timeout = time(NULL) + 3600;
++
++		/* Run timers */
++		then = isns_run_timers();
++		if (then && then < next_timeout)
++			next_timeout = then;
++
++		/* Determine how long we can sleep */
++		now = time(NULL);
++		if (next_timeout <= now)
++			continue;
++		timeout.tv_sec = next_timeout - now;
++
++		if (should_reexport) {
++			load_exported_objects();
++
++			if (register_exported_objects(clnt))
++				isns_error("Failed to register exported objects.\n");
++
++			/* Prime the list of visible storage nodes */
++			if (query_visible())
++				isns_error("Unable to query list of visible nodes.\n");
++			store_imported_objects();
++
++			should_reexport = 0;
++		}
++
++		if ((msg = isns_recv_message(&timeout)) == NULL)
++			continue;
++
++		function = isns_message_function(msg);
++		if (function != ISNS_STATE_CHANGE_NOTIFICATION
++		 && function != ISNS_ENTITY_STATUS_INQUIRY) {
++			isns_warning("Discarding unexpected %s message\n",
++					isns_function_name(function));
++			isns_message_release(msg);
++			continue;
++		}
++
++		if ((resp = isns_process_message(server, msg)) != NULL) {
++			isns_socket_t *sock = isns_message_socket(msg);
++
++			isns_socket_send(sock, resp);
++			isns_message_release(resp);
++		}
++
++		isns_message_release(msg);
++	}
++}
+diff --git a/utils/open-isns/isnssetup b/utils/open-isns/isnssetup
+new file mode 100644
+index 0000000..df0bd00
+--- /dev/null
++++ b/utils/open-isns/isnssetup
+@@ -0,0 +1,52 @@
++#!/bin/sh
++#
++# isnssetup - bootstrap open-isns server
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This is a very simple script to bootstrap an iSNS server.
++# It creates the necessary keys, enrolls a control node,
++# and enrolls the local host as target and initiator.
++
++hostname=`hostname -f`
++
++if [ -f isnsd -a -d isnsadm ]; then
++	PATH=.:$PATH
++fi
++
++# Massage the configuration file
++for f in isnsadm.conf isnsdd.conf; do
++	etcfile=/etc/isns/$f
++	sed -e 's/^#*\(ServerAddress[[:space:]]*=\).*/\1 localhost/' \
++	    -e 's/^#*\(Security[[:space:]]*=\).*/\1 1/' \
++			$etcfile > $etcfile.tmp
++	mv $etcfile.tmp $etcfile
++done
++
++echo "*** Initializing server security ***"
++isnsd --init
++cp /etc/isns/auth_key.pub /etc/isns/server_key.pub
++
++if ps ax|grep isnsd | grep -qv grep; then
++	killall -TERM isnsd
++	sleep 1
++fi
++isnsd
++sleep 1
++
++echo "*** Registering control node policy ***"
++rm -f /etc/isns/control.key
++isnsadm --local \
++	--keyfile=/etc/isns/control.key \
++	--enroll isns.control \
++	node-type=ALL functions=ALL object-type=ALL
++
++echo "*** Registering control node ***"
++isnsadm --local \
++	--register control
++
++echo "*** Registering policy for server ***"
++isnsadm --control \
++	--enroll $hostname \
++	key=/etc/isns/auth_key.pub \
++	node-type=target+initiator
+diff --git a/utils/open-isns/local.c b/utils/open-isns/local.c
+new file mode 100644
+index 0000000..4bc1cb1
+--- /dev/null
++++ b/utils/open-isns/local.c
+@@ -0,0 +1,353 @@
++/*
++ * Local iSNS registration
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ *
++ * The way isnsdd communicates with local services (initiator,
++ * target) is via a file and signals. That sounds rather
++ * awkward, but it's a lot simpler to add to these services
++ * than another socket based communication mechanism I guess.
++ *
++ * The file format is simple:
++ *  <object> owner=<owner>
++ *  <object> owner=<owner>
++ *  ...
++ *
++ * <owner> identifies the service owning these entries.
++ * This is a service name, such as iscsid, tgtd, isnsdd,
++ * optionally followed by a colon and a PID. This allows
++ * removal of all entries created by one service in one go.
++ *
++ * <object> is the description of one iSNS object, using the
++ * syntax used by all other open-isns apps.
++ */
++
++#include <getopt.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <signal.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <limits.h>
++
++#include <isns.h>
++#include "security.h"
++#include "util.h"
++#include "isns-proto.h"
++#include "paths.h"
++#include "attrs.h"
++#include "util.h"
++
++typedef int __isns_local_registry_cb_fn_t(const char *line,
++			int argc, char **argv,
++			void *user_data);
++
++/*
++ * Build the owner=<svcname>:<pid> tag
++ */
++static const char *
++__isns_local_registry_make_owner(const char *svcname, pid_t pid)
++{
++	static char owner[128];
++
++	if (pid == 0) {
++		return svcname;
++	}
++	snprintf(owner, sizeof(owner), "%s:%u", svcname, pid);
++	return owner;
++}
++
++/*
++ * Read the registry file, match each entry against the given owner=<foo> tag,
++ * and invoke the callback function.
++ * This is used for both reading the registry, and rewriting it.
++ */
++static int
++__isns_local_registry_read(const char *match_owner,
++			__isns_local_registry_cb_fn_t handle_matching,
++			__isns_local_registry_cb_fn_t handle_nonmatching,
++			void *user_data)
++{
++	const char	*filename = isns_config.ic_local_registry_file;
++	char		*line, *copy = NULL;
++	FILE		*fp;
++	int		rv = 0, owner_len;
++
++	if (!(fp = fopen(filename, "r"))) {
++		if (errno == ENOENT) {
++			isns_debug_state("Unable to open %s: %m\n", filename);
++			return 1;
++		}
++		isns_error("Unable to open %s: %m\n", filename);
++		return 0;
++	}
++
++	owner_len = match_owner? strlen(match_owner) : 0;
++	while ((line = parser_get_next_line(fp)) != NULL) {
++		__isns_local_registry_cb_fn_t *cb;
++		char	*argv[256], *owner;
++		int	argc = 0;
++
++		isns_assign_string(&copy, line);
++
++		argc = isns_attr_list_split(line, argv, 255);
++		if (argc <= 0)
++			continue;
++
++		/* Last attr should be owner */
++		if (strncasecmp(argv[argc-1], "owner=", 6)) {
++			isns_error("%s: syntax error (missing owner field)\n",
++					filename);
++			goto out;
++		}
++		owner = argv[argc-1] + 6;
++
++		if (!strncasecmp(owner, match_owner, owner_len)
++		 && (owner[owner_len] == '\0' || owner[owner_len] == ':'))
++			cb = handle_matching;
++		else
++			cb = handle_nonmatching;
++
++		if (cb && !cb(copy, argc, argv, user_data))
++			goto out;
++
++	}
++	rv = 1;
++
++out:
++	free(copy);
++	fclose(fp);
++	return rv;
++}
++
++/*
++ * Open and lock the registry file for writing. Returns an
++ * open stream and the name of the lock file.
++ * Follow up with _finish_write when done.
++ */
++static FILE *
++__isns_local_registry_open_write(char **lock_name)
++{
++	char	lock_path[PATH_MAX];
++	FILE	*fp;
++	int	fd, retry;
++
++	snprintf(lock_path, sizeof(lock_path), "%s.lock",
++			isns_config.ic_local_registry_file);
++
++	for (retry = 0; retry < 5; ++retry) {
++		fd = open(lock_path, O_RDWR|O_CREAT|O_EXCL, 0644);
++		if (fd >= 0)
++			break;
++		if (errno != EEXIST) {
++			isns_error("Unable to create %s: %m\n",
++					lock_path);
++			return NULL;
++		}
++		isns_error("Cannot lock %s - retry in 1 sec\n",
++					isns_config.ic_local_registry_file);
++		sleep(1);
++	}
++
++	if (!(fp = fdopen(fd, "w"))) {
++		isns_error("fdopen failed: %m\n");
++		close(fd);
++		return NULL;
++	}
++	isns_assign_string(lock_name, lock_path);
++	return fp;
++}
++
++/*
++ * We're done with (re)writing the registry. Commit the changes,
++ * or discard them.
++ * Also frees the lock_name returned by registry_open_write.
++ */
++static int
++__isns_local_registry_finish_write(FILE *fp, char *lock_name, int commit)
++{
++	int	rv = 1;
++
++	fclose(fp);
++	if (!commit) {
++		if (unlink(lock_name))
++			isns_error("Failed to unlink %s: %m\n", lock_name);
++	} else
++	if (rename(lock_name, isns_config.ic_local_registry_file)) {
++		isns_error("Failed to rename %s to %s: %m\n",
++				lock_name, isns_config.ic_local_registry_file);
++		rv = 0;
++	}
++
++	free(lock_name);
++	return rv;
++}
++
++/*
++ * Get the entity name for this service
++ */
++static char *
++__isns_local_registry_entity_name(const char *owner)
++{
++	static char	namebuf[1024];
++
++	snprintf(namebuf, sizeof(namebuf), "%s:%s",
++			isns_config.ic_entity_name,
++			owner);
++	return namebuf;
++}
++
++/*
++ * Callback function which builds an iSNS object from the
++ * list of attr=tag values.
++ */
++static int
++__isns_local_registry_load_object(const char *line,
++		int argc, char **argv, void *user_data)
++{
++	isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
++	struct isns_attr_list_parser state;
++	isns_object_list_t *list = user_data;
++	isns_object_t *obj, *entity = NULL;
++
++	for (; argc > 0; --argc) {
++		char	*attr = argv[argc-1];
++
++		if (!strncasecmp(attr, "owner=", 6)) {
++			char *eid = __isns_local_registry_entity_name(attr + 6);
++			ISNS_QUICK_ATTR_LIST_DECLARE(key_attrs,
++					ISNS_TAG_ENTITY_IDENTIFIER,
++					string, eid);
++
++			if (entity) {
++				isns_error("Duplicate owner entry in registry\n");
++				continue;
++			}
++			isns_attr_print(&key_attrs.iqa_attr, isns_print_stdout);
++			entity = isns_object_list_lookup(list,
++					&isns_entity_template,
++					&key_attrs.iqa_list);
++			if (entity != NULL)
++				continue;
++
++			isns_debug_state("Creating fake entity %s\n", eid);
++			entity = isns_create_entity(ISNS_ENTITY_PROTOCOL_ISCSI, eid);
++			isns_object_list_append(list, entity);
++		} else {
++			break;
++		}
++	}
++
++	isns_attr_list_parser_init(&state, NULL);
++	if (!isns_parse_attrs(argc, argv, &attrs, &state)) {
++		isns_error("Unable to parse attrs\n");
++		isns_attr_list_destroy(&attrs);
++		return 0;
++	}
++
++	obj = isns_create_object(isns_attr_list_parser_context(&state),
++					&attrs, entity);
++	isns_attr_list_destroy(&attrs);
++
++	if (obj == NULL) {
++		isns_error("Unable to create object\n");
++		return 0;
++	}
++
++	isns_object_list_append(list, obj);
++	return 1;
++}
++
++/*
++ * Callback function that simply writes out the line as-is
++ */
++static int
++__isns_local_registry_rewrite_object(const char *line,
++		int argc, char **argv, void *user_data)
++{
++	FILE	*ofp = user_data;
++
++	fprintf(ofp, "%s\n", line);
++	return 1;
++}
++
++/*
++ * Load all objects owner by a specific service from the local registry.
++ * If the svcname starts with "!", all entries except those matching this
++ * particular service are returned.
++ */
++int
++isns_local_registry_load(const char *svcname, pid_t pid, isns_object_list_t *objs)
++{
++	__isns_local_registry_cb_fn_t *if_matching = NULL, *if_nonmatching = NULL;
++
++	if (svcname == NULL) {
++		isns_error("%s: no svcname given\n", __FUNCTION__);
++		return 0;
++	}
++	if (*svcname == '!') {
++		if_nonmatching = __isns_local_registry_load_object;
++		svcname++;
++	} else {
++		if_matching = __isns_local_registry_load_object;
++	}
++
++	return __isns_local_registry_read(
++			__isns_local_registry_make_owner(svcname, pid),
++			if_matching, if_nonmatching, objs);
++}
++
++/*
++ * Store the given list of objects in the registry.
++ * This replaces all objects previously registered by this service.
++ */
++int
++isns_local_registry_store(const char *svcname, pid_t pid, const isns_object_list_t *objs)
++{
++	const char *owner = __isns_local_registry_make_owner(svcname, pid);
++	char	*lock_name = NULL;
++	FILE	*ofp;
++
++	if (!(ofp = __isns_local_registry_open_write(&lock_name))) {
++		isns_error("%s: could not open registry for writing\n", __FUNCTION__);
++		return 0;
++	}
++
++	/* First, purge all entries previously belonging to this owner */
++	if (!__isns_local_registry_read(owner, NULL, __isns_local_registry_rewrite_object, ofp))
++		goto failed;
++
++	if (objs) {
++		unsigned int	i;
++
++		for (i = 0; i < objs->iol_count; ++i) {
++			isns_object_t *obj = objs->iol_data[i];
++			char *argv[256];
++			int i, argc;
++
++			argc = isns_print_attrs(obj, argv, 256);
++			for (i = 0; i < argc; ++i)
++				fprintf(ofp, "%s ", argv[i]);
++			fprintf(ofp, "owner=%s\n", owner);
++		}
++	}
++
++	return __isns_local_registry_finish_write(ofp, lock_name, 1);
++
++failed:
++	isns_error("%s: error rewriting registry file\n", __FUNCTION__);
++	__isns_local_registry_finish_write(ofp, lock_name, 0);
++	return 0;
++}
++
++/*
++ * Purge the local registry of all objects owned by the
++ * given service.
++ */
++int
++isns_local_registry_purge(const char *svcname, pid_t pid)
++{
++	return isns_local_registry_store(svcname, pid, NULL);
++}
+diff --git a/utils/open-isns/logging.c b/utils/open-isns/logging.c
+new file mode 100644
+index 0000000..63ebbef
+--- /dev/null
++++ b/utils/open-isns/logging.c
+@@ -0,0 +1,228 @@
++/*
++ * Logging related utility functions.
++ *
++ * Copyright (C) 2004-2007 Olaf Kirch <okir@suse.de>
++ */
++
++#include <stdio.h>
++#include <stdarg.h>
++#include <stdlib.h>
++#include <string.h>
++#include <syslog.h>
++
++#include "util.h"
++
++static unsigned int	log_stdout = 1;
++static unsigned int	debugging = 0;
++
++/*
++ * When backgrounding, any logging output should
++ * go to syslog instead of stdout
++ */
++void
++isns_log_background(void)
++{
++	log_stdout = 0;
++}
++
++/*
++ * For output to syslog, sanitize the format string
++ * by removing newlines.
++ */
++static const char *
++sanitize_format(const char *fmt)
++{
++	static char	__fmt[1024];
++	unsigned int	len;
++
++	/* Don't bother unless there's a newline */
++	if (!strchr(fmt, '\n'))
++		return fmt;
++
++	len = strlen(fmt);
++
++	/* Decline if the buffer would overflow */
++	if (len >= sizeof(__fmt))
++		return fmt;
++
++	strcpy(__fmt, fmt);
++	while (len-- && __fmt[len] == '\n')
++		__fmt[len] = '\0';
++
++	while (len) {
++		if (__fmt[len] == '\n')
++			__fmt[len] = ' ';
++		--len;
++	}
++
++	return __fmt;
++}
++
++/*
++ * Output to stderr or syslog
++ */
++static void
++voutput(int severity, const char *fmt, va_list ap)
++{
++	if (log_stdout) {
++		switch (severity) {
++		case LOG_ERR:
++			fprintf(stderr, "Error: ");
++			break;
++		case LOG_WARNING:
++			fprintf(stderr, "Warning: ");
++			break;
++		case LOG_DEBUG:
++			fprintf(stderr, "   ");
++			break;
++		}
++		vfprintf(stderr, fmt, ap);
++	} else {
++		fmt = sanitize_format(fmt);
++		if (!fmt || !*fmt)
++			return;
++		vsyslog(severity, fmt, ap);
++	}
++}
++
++void
++isns_assert_failed(const char *condition, const char *file, unsigned int line)
++{
++	isns_error("Assertion failed (%s:%d): %s\n",
++			file, line, condition);
++	abort();
++}
++
++void
++isns_fatal(const char *fmt, ...)
++{
++	va_list	ap;
++
++	va_start(ap, fmt);
++	if (log_stdout)
++		fprintf(stderr, "** FATAL ERROR **\n");
++	voutput(LOG_ERR, fmt, ap);
++	va_end(ap);
++	exit(1);
++}
++
++void
++isns_error(const char *fmt, ...)
++{
++	va_list	ap;
++
++	va_start(ap, fmt);
++	voutput(LOG_WARNING, fmt, ap);
++	va_end(ap);
++}
++
++void
++isns_warning(const char *fmt, ...)
++{
++	va_list	ap;
++
++	va_start(ap, fmt);
++	voutput(LOG_NOTICE, fmt, ap);
++	va_end(ap);
++}
++
++void
++isns_notice(const char *fmt, ...)
++{
++	va_list	ap;
++
++	va_start(ap, fmt);
++	voutput(LOG_INFO, fmt, ap);
++	va_end(ap);
++}
++
++void
++isns_enable_debugging(const char *what)
++{
++	char	*copy, *s, *next;
++
++	if (!strcmp(what, "all")) {
++		debugging = ~0U;
++		return;
++	}
++	
++	copy = isns_strdup(what);
++	
++	for (s = copy; s; s = next) {
++		if ((next = strchr(s, ',')) != NULL)
++			*next++ = '\0';
++
++		if (!strcmp(s, "general"))
++			debugging |= (1 << DBG_GENERAL);
++		else if (!strcmp(s, "socket"))
++			debugging |= (1 << DBG_SOCKET);
++		else if (!strcmp(s, "protocol"))
++			debugging |= (1 << DBG_PROTOCOL);
++		else if (!strcmp(s, "state"))
++			debugging |= (1 << DBG_STATE);
++		else if (!strcmp(s, "message"))
++			debugging |= (1 << DBG_MESSAGE);
++		else if (!strcmp(s, "auth"))
++			debugging |= (1 << DBG_AUTH);
++		else if (!strcmp(s, "scn"))
++			debugging |= (1 << DBG_SCN);
++		else if (!strcmp(s, "esi"))
++			debugging |= (1 << DBG_ESI);
++		else {
++			isns_error("Ignoring unknown isns_debug facility <<%s>>\n",
++					s);
++		}
++	}
++	isns_free(copy);
++}
++
++#define DEFINE_DEBUG_FUNC(name, NAME) \
++void						\
++isns_debug_##name(const char *fmt, ...)		\
++{						\
++	va_list	ap;				\
++						\
++	if (!(debugging & (1 << DBG_##NAME)))	\
++		return;				\
++						\
++	va_start(ap, fmt);			\
++	voutput(LOG_DEBUG, fmt, ap);		\
++	va_end(ap);				\
++}
++DEFINE_DEBUG_FUNC(general,	GENERAL)
++DEFINE_DEBUG_FUNC(socket,	SOCKET)
++DEFINE_DEBUG_FUNC(protocol,	PROTOCOL)
++DEFINE_DEBUG_FUNC(message,	MESSAGE)
++DEFINE_DEBUG_FUNC(auth,		AUTH)
++DEFINE_DEBUG_FUNC(state,	STATE)
++DEFINE_DEBUG_FUNC(scn,		SCN)
++DEFINE_DEBUG_FUNC(esi,		ESI)
++
++int
++isns_debug_enabled(int fac)
++{
++	return (debugging & (1 << fac)) != 0;
++}
++
++/*
++ * Misc isns_print_fn_t implementations
++ */
++void
++isns_print_stdout(const char *fmt, ...)
++{
++	va_list	ap;
++
++	va_start(ap, fmt);
++	vfprintf(stdout, fmt, ap);
++	va_end(ap);
++}
++
++void
++isns_print_stderr(const char *fmt, ...)
++{
++	va_list	ap;
++
++	va_start(ap, fmt);
++	vfprintf(stderr, fmt, ap);
++	va_end(ap);
++}
+diff --git a/utils/open-isns/mdebug.c b/utils/open-isns/mdebug.c
+new file mode 100644
+index 0000000..90dcaf0
+--- /dev/null
++++ b/utils/open-isns/mdebug.c
+@@ -0,0 +1,295 @@
++/*
++ * Stupid malloc debugger. I think I wrote something like
++ * this a couple of times already. Where does all the old
++ * source code go?
++ */
++
++#ifdef MDEBUG
++
++#include <stdlib.h>
++#include <string.h>
++#include "util.h"
++
++static void *		isns_malloc_default(size_t, const char *, unsigned int);
++static void *		isns_calloc_default(unsigned int, size_t,
++				const char *, unsigned int);
++static void *		isns_realloc_default(void *, size_t,
++				const char *, unsigned int);
++static char *		isns_strdup_default(const char *, const char *, unsigned int);
++static void		isns_free_default(void *, const char *, unsigned int);
++
++/*
++ * These are the function pointers used to redirect malloc and such.
++ */
++void *			(*isns_malloc_fn)(size_t, const char *, unsigned int) = isns_malloc_default;
++void *			(*isns_calloc_fn)(unsigned int, size_t,
++				const char *, unsigned int) = isns_calloc_default;
++void *			(*isns_realloc_fn)(void *, size_t,
++				const char *, unsigned int) = isns_realloc_default;
++char *			(*isns_strdup_fn)(const char *, const char *, unsigned int) = isns_strdup_default;
++void			(*isns_free_fn)(void *, const char *, unsigned int) = isns_free_default;
++
++#define H_MAGIC		0xfeedbeef
++#define T_MAGIC		0xbadf00d
++#define CHUNK_OVERHEAD	(sizeof(struct m_header) + sizeof(struct m_trailer))
++
++struct m_header {
++	struct isns_list	h_list;
++	uint32_t		h_magic;
++	size_t			h_size;
++
++	const char *		h_file;
++	unsigned int		h_line;
++};
++
++struct m_trailer {
++	uint32_t		t_magic[8];
++	size_t			t_size;
++};
++
++static ISNS_LIST_DECLARE(m_list);
++static void *			m_low_addr;
++static void *			m_high_addr;
++static int			m_init = 0;
++
++static void
++__isns_check_chunk(const struct m_header *head)
++{
++	const struct m_trailer *tail;
++	int		i;
++
++	if ((void *) head < m_low_addr
++	 || (void *) head > m_high_addr) {
++		isns_error("%s: m_list corrupted!\n", __FUNCTION__);
++		abort();
++	}
++
++	if (head->h_magic != H_MAGIC) {
++		isns_error("%s: m_list item %p with bad header magic %08x\n",
++				__FUNCTION__, head, head->h_magic);
++		isns_error("    Allocated from %s:%u\n",
++				head->h_file, head->h_line);
++		abort();
++	}
++
++	tail = ((void *) head) + sizeof(*head) + head->h_size;
++	for (i = 0; i < 8; ++i) {
++		if (tail->t_magic[i] == T_MAGIC)
++			continue;
++
++		isns_error("%s: m_list item %p with bad trailer magic[%d] %08x\n",
++				__FUNCTION__, head, i, tail->t_magic[i]);
++		isns_error("    Allocated from %s:%u\n",
++				head->h_file, head->h_line);
++		abort();
++	}
++
++	if (tail->t_size != head->h_size) {
++		isns_error("%s: m_list item %p size mismatch; head=%u tail=%u\n",
++				__FUNCTION__, head,
++				head->h_size, tail->t_size);
++		isns_error("    Allocated from %s:%u\n",
++				head->h_file, head->h_line);
++		abort();
++	}
++}
++
++static void
++__isns_verify_all(void)
++{
++	struct isns_list	*pos, *next;
++
++	isns_list_foreach(&m_list, pos, next) {
++		__isns_check_chunk(isns_list_item(struct m_header, h_list, pos));
++	}
++}
++
++void *
++__isns_malloc(size_t size, const char *file, unsigned int line)
++{
++	struct m_header	*head;
++	struct m_trailer *tail;
++	size_t		true_size;
++	void		*ptr;
++	int		i;
++
++	__isns_verify_all();
++
++	true_size = size + sizeof(*head) + sizeof(*tail);
++	isns_assert(size < true_size);
++
++	ptr = malloc(true_size);
++	if (!ptr)
++		return NULL;
++
++	if (!m_low_addr) {
++		m_low_addr = m_high_addr = ptr;
++	} else if (ptr < m_low_addr) {
++		m_low_addr = ptr;
++	} else if (ptr > m_high_addr) {
++		m_high_addr = ptr;
++	}
++
++	head = ptr;
++	head->h_magic = H_MAGIC;
++	head->h_size = size;
++	head->h_file = file;
++	head->h_line = line;
++	isns_list_append(&m_list, &head->h_list);
++
++	ptr += sizeof(*head);
++
++	tail = ptr + size;
++	for (i = 0; i < 8; ++i)
++		tail->t_magic[i] = T_MAGIC;
++	tail->t_size = size;
++
++	return ptr;
++}
++
++void *
++__isns_calloc(unsigned int nele, size_t size,
++		const char *file, unsigned int line)
++{
++	void	*ptr;
++
++	ptr = __isns_malloc(nele * size, file, line);
++	if (ptr)
++		memset(ptr, 0, nele * size);
++	return ptr;
++}
++
++void *
++__isns_realloc(void *old, size_t new_size,
++		const char *file, unsigned int line)
++{
++	struct m_header *old_head = NULL;
++	void	*new;
++
++	if (old) {
++		old_head = (old - sizeof(struct m_header));
++		__isns_check_chunk(old_head);
++	}
++
++	new = __isns_malloc(new_size, file, line);
++	if (new && old) {
++		memcpy(new, old, old_head->h_size);
++		isns_free_fn(old, file, line);
++	}
++
++	return new;
++}
++
++
++char *
++__isns_strdup(const char *s, const char *file, unsigned int line)
++{
++	size_t	len;
++	char	*ptr;
++
++	len = s? strlen(s) : 0;
++	ptr = __isns_malloc(len + 1, file, line);
++	if (ptr) {
++		memcpy(ptr, s, len);
++		ptr[len] = '\0';
++	}
++	return ptr;
++}
++
++void
++__isns_free(void *ptr, const char *file, unsigned int line)
++{
++	struct m_header	*head;
++	size_t	true_size;
++
++	if (ptr == NULL)
++		return;
++
++	head = ptr - sizeof(struct m_header);
++	__isns_check_chunk(head);
++
++	/*
++	printf("__isns_free(%u from %s:%u): freed by %s:%u\n",
++			head->h_size, head->h_file, head->h_line,
++			file, line);
++	   */
++	true_size = head->h_size + CHUNK_OVERHEAD;
++	isns_list_del(&head->h_list);
++
++	memset(head, 0xa5, true_size);
++	free(head);
++
++	__isns_verify_all();
++}
++
++/*
++ * Enable memory debugging
++ */
++static void
++__isns_mdebug_init(void)
++{
++	const char	*tracefile;
++
++	tracefile = getenv("ISNS_MTRACE");
++	if (tracefile)
++		isns_error("MTRACE not yet supported\n");
++
++	if (getenv("ISNS_MDEBUG")) {
++		isns_malloc_fn = __isns_malloc;
++		isns_calloc_fn = __isns_calloc;
++		isns_realloc_fn = __isns_realloc;
++		isns_strdup_fn = __isns_strdup;
++		isns_free_fn = __isns_free;
++		isns_notice("Enabled memory debugging\n");
++	}
++
++	m_init = 1;
++}
++
++static inline void
++isns_mdebug_init(void)
++{
++	if (!m_init)
++		__isns_mdebug_init();
++}
++
++/*
++ * Default implementations of malloc and friends
++ */
++static void *
++isns_malloc_default(size_t size, const char *file, unsigned int line)
++{
++	isns_mdebug_init();
++	return malloc(size);
++}
++
++static void *
++isns_calloc_default(unsigned int nele, size_t size,
++				const char *file, unsigned int line)
++{
++	isns_mdebug_init();
++	return calloc(nele, size);
++}
++
++static void *
++isns_realloc_default(void *old, size_t size,
++				const char *file, unsigned int line)
++{
++	isns_mdebug_init();
++	return realloc(old, size);
++}
++
++static char *
++isns_strdup_default(const char *s, const char *file, unsigned int line)
++{
++	isns_mdebug_init();
++	return strdup(s);
++}
++
++static void
++isns_free_default(void *ptr, const char *file, unsigned int line)
++{
++	isns_mdebug_init();
++	return free(ptr);
++}
++#endif
+diff --git a/utils/open-isns/message.c b/utils/open-isns/message.c
+new file mode 100644
+index 0000000..4cd40c3
+--- /dev/null
++++ b/utils/open-isns/message.c
+@@ -0,0 +1,681 @@
++/*
++ * iSNS message handling functions
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ *
++ *
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <sys/time.h>	/* for timercmp */
++#include <unistd.h>	/* gethostname */
++#include <ctype.h>
++#include "isns.h"
++#include "attrs.h"
++#include "message.h"
++#include "socket.h"
++#include "util.h"
++
++/* iSCSI qualified names include the year and
++ * month in which the domain was assigned.
++ * See RFC 3720, section 3.2.6.3.1.
++ * That's one of these wonderful committee
++ * type of ideas that makes it hard for everyone,
++ * from coder to sysadmin.
++ * Since we have no way of finding out here,
++ * we fake it by assigning a date before the
++ * dawn of time.
++ */
++#define DUMMY_IQN_PREFIX	"iqn.1967-12."
++
++static uint32_t		isns_xid = 1;
++
++/*
++ * Initialize a message object
++ */
++isns_message_t *
++__isns_alloc_message(uint32_t xid, size_t size, void (*destroy)(isns_message_t *))
++{
++	isns_message_t	*msg;
++
++	isns_assert(size >= sizeof(*msg));
++	msg = isns_calloc(1, size);
++
++	isns_list_init(&msg->im_list);
++	msg->im_users = 1;
++	msg->im_xid = xid;
++	msg->im_destroy = destroy;
++
++	return msg;
++}
++
++static int
++__isns_message_init(isns_message_t *msg,
++			uint16_t function, uint16_t flags,
++			size_t payload_len)
++{
++	struct isns_hdr *hdr = &msg->im_header;
++
++	/* Pad to multiple of 4 octets */
++	payload_len = (payload_len + 3) & ~3UL;
++
++	/* For now, we don't do segmentation */
++	if (payload_len > ISNS_MAX_PDU_SIZE)
++		return 0;
++
++	/* msg->im_header is in host byte order */
++	hdr->i_version = ISNS_VERSION;
++	hdr->i_function = function;
++	hdr->i_flags = flags;
++	hdr->i_length = payload_len;
++	hdr->i_xid = msg->im_xid;
++	hdr->i_seq = 0;
++
++	/* Allocate buffer and reserve room for header */
++	msg->im_payload = buf_alloc(sizeof(*hdr) + payload_len);
++	buf_push(msg->im_payload, sizeof(*hdr));
++
++	return 1;
++}
++
++/*
++ * Allocate a message object.
++ */
++static isns_message_t *
++__isns_create_message(uint32_t xid, uint16_t function, uint16_t flags)
++{
++	isns_message_t *msg;
++
++	msg = __isns_alloc_message(xid, sizeof(*msg), NULL);
++	__isns_message_init(msg, function, flags, ISNS_MAX_MESSAGE);
++
++	return msg;
++}
++
++/*
++ * Allocate a request message
++ */
++isns_message_t *
++isns_create_message(uint16_t function, uint16_t flags)
++{
++	return __isns_create_message(isns_xid++, function, flags);
++}
++
++/*
++ * Allocate a response message
++ */
++isns_message_t *
++isns_create_reply(const isns_message_t *msg)
++{
++	uint16_t function = msg->im_header.i_function;;
++	isns_message_t	*resp;
++
++	resp = __isns_create_message(msg->im_xid, function | 0x8000, ISNS_F_SERVER);
++	resp->im_addr = msg->im_addr;
++	resp->im_addrlen = msg->im_addrlen;
++
++	/* Default to ISNS_SUCCESS */
++	buf_put32(resp->im_payload, ISNS_SUCCESS);
++
++	return resp;
++}
++
++/*
++ * Delete a message
++ */
++void
++isns_message_release(isns_message_t *msg)
++{
++	if (msg == NULL)
++		return;
++
++	isns_assert(msg->im_users);
++	if (--(msg->im_users))
++		return;
++
++	if (msg->im_destroy)
++		msg->im_destroy(msg);
++	if (msg->im_payload)
++		buf_free(msg->im_payload);
++	isns_principal_free(msg->im_security);
++
++	isns_list_del(&msg->im_list);
++	isns_free(msg);
++}
++
++/*
++ * Extract the status from a reply message
++ */
++int
++isns_message_status(isns_message_t *msg)
++{
++	uint32_t	status;
++
++	if (!(msg->im_header.i_function & 0x8000)
++	 || !buf_get32(msg->im_payload, &status))
++		return ISNS_MESSAGE_FORMAT_ERROR;
++	return status;
++}
++
++/*
++ * Obtain the socket on which the message was received.
++ */
++isns_socket_t *
++isns_message_socket(const isns_message_t *msg)
++{
++	return msg->im_socket;
++}
++
++/*
++ * Obtain the message's security context
++ */
++isns_security_t *
++isns_message_security(const isns_message_t *msg)
++{
++	if (!msg->im_socket)
++		return NULL;
++	return msg->im_socket->is_security;
++}
++
++unsigned int
++isns_message_function(const isns_message_t *msg)
++{
++	return msg->im_header.i_function;
++}
++
++/*
++ * Reset the response message, and encode isns_error
++ * status
++ */
++void
++isns_message_set_error(isns_message_t *msg, uint32_t status)
++{
++	/* Clear the buffer. This just resets head + tail */
++	buf_clear(msg->im_payload);
++
++	/* Now move past the header, and overwrite the
++	 * status word. */
++	buf_push(msg->im_payload, sizeof(struct isns_hdr));
++	buf_put32(msg->im_payload, status);
++}
++
++/*
++ * Message queue handling. Most related functions are
++ * in message.h
++ */
++void
++isns_message_queue_move(isns_message_queue_t *dstq,
++		isns_message_t *msg)
++{
++	unsigned int	src_ref = 0;
++
++	/* If the message was on a different queue,
++	 * the source queue will hold a reference
++	 * to it. Account for that and fix up the
++	 * refcount after we've appended it to the
++	 * destination queue. */
++	if (isns_message_unlink(msg))
++		src_ref = 1;
++
++	isns_message_queue_append(dstq, msg);
++	msg->im_users -= src_ref;
++}
++
++/*
++ * Insert a messsage into a queue sorted by resend timeout
++ */
++void
++isns_message_queue_insert_sorted(isns_message_queue_t *q,
++		int sort, isns_message_t *msg)
++{
++	isns_list_t	*pos;
++	isns_message_t	*__m;
++
++	isns_assert(msg->im_queue == NULL);
++	if (sort == ISNS_MQ_SORT_RESEND_TIMEOUT) {
++		isns_message_queue_foreach(q, pos, __m) {
++			if (timercmp(&msg->im_resend_timeout,
++				     &__m->im_resend_timeout, <))
++				break;
++		}
++	} else {
++		isns_message_queue_append(q, msg);
++		return;
++	}
++
++	/* Insert before pos */
++	__isns_list_insert(pos->prev, &msg->im_list, pos);
++	q->imq_count++;
++
++	msg->im_queue = q;
++	msg->im_users++;
++}
++
++/*
++ * Message queue handling
++ */
++void
++isns_message_queue_destroy(isns_message_queue_t *q)
++{
++	isns_message_t	*msg;
++
++	while ((msg = isns_message_dequeue(q)) != NULL)
++		isns_message_release(msg);
++}
++
++/*
++ * Find a message with matching xid and address.
++ * (address, alen) may be NULL.
++ */
++isns_message_t *
++isns_message_queue_find(isns_message_queue_t *q, uint32_t xid,
++		const struct sockaddr_storage *addr, socklen_t alen)
++{
++	isns_message_t	*msg;
++	isns_list_t	*pos;
++
++	isns_message_queue_foreach(q, pos, msg) {
++		if (msg->im_xid != xid)
++			continue;
++		if (alen == 0)
++			return msg;
++
++		if (msg->im_addrlen == alen
++		 && !memcmp(&msg->im_addr, addr, alen))
++			return msg;
++	}
++
++	return NULL;
++}
++
++/*
++ * Convert a hostname into an iSCSI qualified name
++ * We omit the dismbiguating YYYY-MM infix because
++ * we have no way of finding out, short of bothering
++ * whois.
++ */
++static char *
++__revert_fqdn(const char *prefix, const char *__fqdn, const char *suffix)
++{
++	static char	namebuf[1024] = { '\0' };
++	char		*fqdn, *result = NULL;
++	int		pos, count = 0;
++
++	if (prefix)
++		strcpy(namebuf, prefix);
++	pos = strlen(namebuf);
++
++	fqdn = isns_strdup(__fqdn);
++	while (1) {
++		char	*dot, *comp;
++		int	comp_len;
++
++		if ((dot = strrchr(fqdn, '.')) != NULL) {
++			*dot++ = '\0';
++			comp = dot;
++		} else {
++			comp = fqdn;
++		}
++
++		if (*comp == '\0')
++			continue;
++		comp_len = strlen(comp);
++		if (pos + comp_len + 2 > sizeof(namebuf)) {
++			isns_error("%s: FQDN too long\n", __FUNCTION__);
++			goto out;
++		}
++		if (count++)
++			namebuf[pos++] = '.';
++		strcpy(namebuf + pos, comp);
++		pos += comp_len;
++
++		if (dot == NULL)
++			break;
++	}
++
++	if (suffix) {
++		int	sfx_len = strlen(suffix);
++
++		if (pos + sfx_len + 2 > sizeof(namebuf)) {
++			isns_error("%s: name too long\n", __FUNCTION__);
++			goto out;
++		}
++		namebuf[pos++] = ':';
++		strcpy(namebuf + pos, suffix);
++		pos += sfx_len;
++	}
++
++	result = isns_strdup(namebuf);
++
++out:	isns_free(fqdn);
++	return result;
++}
++
++/*
++ * Initialize all names
++ */
++int
++isns_init_names(void)
++{
++	if (isns_config.ic_host_name == NULL) {
++		char	namebuf[1024], *fqdn;
++
++		if (gethostname(namebuf, sizeof(namebuf)) < 0) {
++			isns_error("gehostname: %m\n");
++			return 0;
++		}
++		fqdn = isns_get_canon_name(namebuf);
++		if (fqdn == NULL) {
++			/* FIXME: we could get some unique value here
++			 * such as the IP address, and concat that
++			 * with iqn.2005-01.org.open-iscsi.ip for the
++			 * source name.
++			 */
++			isns_error("Unable to get fully qualified hostname\n");
++			return 0;
++		}
++		isns_config.ic_host_name = fqdn;
++	}
++
++	if (isns_config.ic_auth_name == NULL) {
++		isns_config.ic_auth_name = isns_config.ic_host_name;
++	}
++
++	if (isns_config.ic_entity_name == NULL) {
++		isns_config.ic_entity_name = isns_config.ic_auth_name;
++	}
++
++	if (isns_config.ic_source_name == NULL) {
++		isns_config.ic_source_name = __revert_fqdn(DUMMY_IQN_PREFIX,
++				isns_config.ic_host_name,
++				isns_config.ic_source_suffix);
++		if (isns_config.ic_source_name == NULL) {
++			isns_error("Unable to build source name\n");
++			return 0;
++		}
++	}
++
++	return 1;
++}
++
++/*
++ * Match a source name to a pattern (which is really just
++ * the entity identifier, usually).
++ *
++ * If the pattern is of the form "match:rev-fqdn", the
++ * source name must match
++ *	iqn.[YYYY-MM.]<rev-fqdn>
++ * optionally followed by dot, colon or hyphen and arbitrary
++ * text.
++ *
++ * If the pattern does not start with "match:", the source name
++ * must match the pattern literally (case insensitively).
++ */
++int
++isns_source_pattern_match(const char *pattern, const char *source)
++{
++	unsigned int	rev_len;
++
++	isns_debug_message("%s(%s, %s)\n",
++			__FUNCTION__, pattern, source);
++
++	if (!strcmp(pattern, "*"))
++		return 1;
++
++	if (strncmp(pattern, "match:", 6))
++		return !strcasecmp(pattern, source);
++	pattern += 6;
++
++	if (strncasecmp(source, "iqn.", 4))
++		return 0;
++	source += 4;
++
++	rev_len = strlen(pattern);
++	if (strncasecmp(source, pattern, rev_len)) {
++		/* See if the next component is YYYY-MM */
++		if (!(isdigit(source[0])
++		   && isdigit(source[1])
++		   && isdigit(source[2])
++		   && isdigit(source[3])
++		   && source[4] == '-'
++		   && isdigit(source[5])
++		   && isdigit(source[6])
++		   && source[7] == '.'))
++			return 0;
++		source += 8;
++
++		if (strncasecmp(source, pattern, rev_len))
++			return 0;
++	}
++
++	source += rev_len;
++	if (source[0] != '.'
++	 && source[0] != ':'
++	 && source[0] != '-'
++	 && source[0] != '\0')
++		return 0;
++
++	return 1;
++}
++
++/*
++ * This really just reverts the FQDN so it can
++ * be used in isns_source_entity_match
++ */
++char *
++isns_build_source_pattern(const char *fqdn)
++{
++	return __revert_fqdn("match:", fqdn, NULL);
++}
++
++/*
++ * Manage source objects
++ */
++static isns_source_t *
++__isns_source_create(isns_attr_t *name_attr)
++{
++	isns_source_t	*source = isns_calloc(1, sizeof(*source));
++
++	source->is_users = 1;
++	source->is_attr = name_attr;
++	return source;
++}
++
++isns_source_t *
++isns_source_create(isns_attr_t *name_attr)
++{
++	if (name_attr->ia_tag_id != ISNS_TAG_ISCSI_NAME
++	 && name_attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN)
++		return NULL;
++
++	name_attr->ia_users++;
++	return __isns_source_create(name_attr);
++}
++
++isns_source_t *
++isns_source_from_object(const isns_object_t *node)
++{
++	isns_attr_t	*attr;
++
++	if (!(attr = isns_storage_node_key_attr(node)))
++		return NULL;
++	return isns_source_create(attr);
++}
++
++isns_source_t *
++isns_source_create_iscsi(const char *name)
++{
++	isns_value_t	var = ISNS_VALUE_INIT(string, (char *) name);
++	isns_attr_t	*attr;
++
++	attr = isns_attr_alloc(ISNS_TAG_ISCSI_NAME, NULL, &var);
++	return __isns_source_create(attr);
++}
++
++/*
++ * This is used to attach a dummy source to iSNS responses
++ * until I fixed up all the code that relies on msg->is_source
++ * to be valid all the time.
++ */
++isns_source_t *
++isns_source_dummy(void)
++{
++	static isns_source_t *dummy = NULL;
++
++	if (!dummy)
++		dummy = isns_source_create_iscsi(".dummy.");
++	return isns_source_get(dummy);
++}
++
++uint32_t
++isns_source_type(const isns_source_t *source)
++{
++	return source->is_attr->ia_tag_id;
++}
++
++const char *
++isns_source_name(const isns_source_t *source)
++{
++	return source->is_attr->ia_value.iv_string;
++}
++
++isns_attr_t *
++isns_source_attr(const isns_source_t *source)
++{
++	return source->is_attr;
++}
++
++/*
++ * Obtain an additional reference on the source object
++ */
++isns_source_t *
++isns_source_get(isns_source_t *source)
++{
++	if (source)
++		source->is_users++;
++	return source;
++}
++
++/*
++ * Look up the node corresponding to this source name
++ * When we get here, we have already verified that the
++ * client is permitted (by policy) to use this source node.
++ */
++int
++isns_source_set_node(isns_source_t *source, isns_db_t *db)
++{
++	isns_object_t	*node, *entity;
++	uint32_t	node_type;
++
++	if (source->is_node)
++		return 1;
++
++	if (db == NULL)
++		return 0;
++
++	node = isns_db_lookup_source_node(db, source);
++	if (node == NULL)
++		return 0;
++
++	if (!isns_object_get_uint32(node, ISNS_TAG_ISCSI_NODE_TYPE, &node_type))
++		node_type = 0;
++
++	source->is_node = node;
++	source->is_node_type = node_type;
++
++	if ((entity = isns_object_get_entity(node)) != NULL)
++		source->is_entity = isns_object_get(entity);
++	return 1;
++}
++
++void
++isns_source_set_entity(isns_source_t *source, isns_object_t *obj)
++{
++	if (obj)
++		isns_object_get(obj);
++	isns_object_release(source->is_entity);
++	source->is_entity = obj;
++}
++
++/*
++ * Release a reference on the source object
++ */
++void
++isns_source_release(isns_source_t *source)
++{
++	if (source && --source->is_users == 0) {
++		isns_attr_release(source->is_attr);
++		isns_object_release(source->is_node);
++		isns_object_release(source->is_entity);
++		memset(source, 0xa5, sizeof(*source));
++		isns_free(source);
++	}
++}
++
++/*
++ * Compare two source objects
++ */
++int
++isns_source_match(const isns_source_t *a,
++		const isns_source_t *b)
++{
++	if (a && b)
++		return isns_attr_match(a->is_attr, b->is_attr);
++	return 0;
++}
++
++/*
++ * Encode/decode source object
++ */
++int
++isns_source_encode(buf_t *bp, const isns_source_t *source)
++{
++	if (source == NULL) {
++		isns_attr_t nil = ISNS_ATTR_INIT(ISNS_TAG_DELIMITER, nil, 0);
++		
++		return isns_attr_encode(bp, &nil);
++	}
++	return isns_attr_encode(bp, source->is_attr);
++}
++
++int
++isns_source_decode(buf_t *bp, isns_source_t **result)
++{
++	isns_attr_t	*attr;
++	int		status;
++
++	status = isns_attr_decode(bp, &attr);
++	if (status == ISNS_SUCCESS) {
++		/*
++		 * 5.6.1
++		 * The Source Attribute uniquely identifies the source of the
++		 * message.  Valid Source Attribute types are shown below.
++		 *
++		 *	Valid Source Attributes
++		 *	-----------------------
++		 *	iSCSI Name
++		 *	FC Port Name WWPN
++		 */
++		switch (attr->ia_tag_id) {
++#if 0
++		case ISNS_TAG_DELIMITER:
++			*result = NULL;
++			break;
++#endif
++
++		case ISNS_TAG_ISCSI_NAME:
++			*result = __isns_source_create(attr);
++			break;
++
++		case ISNS_TAG_FC_PORT_NAME_WWPN:
++			*result = __isns_source_create(attr);
++			break;
++
++		default:
++			isns_attr_release(attr);
++			return ISNS_SOURCE_UNKNOWN;
++		}
++	}
++	return status;
++}
+diff --git a/utils/open-isns/message.h b/utils/open-isns/message.h
+new file mode 100644
+index 0000000..f1f4ed6
+--- /dev/null
++++ b/utils/open-isns/message.h
+@@ -0,0 +1,196 @@
++/*
++ * iSNS message definitions and functions
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_MESSAGE_H
++#define ISNS_MESSAGE_H
++
++#include "attrs.h"
++#include "source.h"
++#include "util.h"
++
++typedef struct isns_message_queue isns_message_queue_t;
++
++struct isns_simple {
++	uint32_t		is_function;
++	isns_source_t *		is_source;
++	isns_policy_t *		is_policy;
++	uint16_t		is_xid;
++
++	unsigned int		is_replace : 1;
++
++	isns_attr_list_t	is_message_attrs;
++	isns_attr_list_t	is_operating_attrs;
++};
++
++struct isns_message {
++	unsigned int		im_users;
++	isns_list_t		im_list;
++	struct sockaddr_storage	im_addr;
++	socklen_t		im_addrlen;
++	uint32_t		im_xid;
++	struct isns_hdr		im_header;
++	struct isns_buf *	im_payload;
++	isns_socket_t *		im_socket;
++	isns_principal_t *	im_security;
++	struct ucred *		im_creds;
++
++	isns_message_queue_t *	im_queue;
++
++	/* When to retransmit */
++	struct timeval		im_resend_timeout;
++	struct timeval		im_timeout;
++
++	void			(*im_destroy)(isns_message_t *);
++	void			(*im_callback)(isns_message_t *,
++					isns_message_t *);
++	void *			im_calldata;
++};
++
++enum {
++	ISNS_MQ_SORT_NONE,
++	ISNS_MQ_SORT_RESEND_TIMEOUT,
++};
++
++struct isns_message_queue {
++	isns_list_t		imq_list;
++	size_t			imq_count;
++};
++
++struct isns_server {
++	isns_source_t *		is_source;
++	isns_db_t *		is_db;
++
++	isns_scn_callback_fn_t *is_scn_callback;
++	struct isns_service_ops *is_ops;
++};
++
++extern isns_message_t *	__isns_alloc_message(uint32_t, size_t, void (*)(isns_message_t *));
++extern isns_security_t *isns_message_security(const isns_message_t *);
++
++extern isns_message_t *	isns_message_queue_find(isns_message_queue_t *, uint32_t,
++				const struct sockaddr_storage *, socklen_t);
++extern void		isns_message_queue_insert_sorted(isns_message_queue_t *,
++				int, isns_message_t *);
++extern void		isns_message_queue_move(isns_message_queue_t *,
++				isns_message_t *);
++extern void		isns_message_queue_destroy(isns_message_queue_t *);
++
++extern isns_simple_t *	isns_simple_create(uint32_t,
++				isns_source_t *,
++				const isns_attr_list_t *);
++extern void		isns_simple_free(isns_simple_t *);
++extern int		isns_simple_encode(isns_simple_t *,
++				isns_message_t **result);
++extern int		isns_simple_decode(isns_message_t *,
++				isns_simple_t **);
++extern int		isns_simple_encode_response(isns_simple_t *,
++				const isns_message_t *, isns_message_t **);
++extern int		isns_simple_response_get_objects(isns_simple_t *,
++				isns_object_list_t *);
++extern const char *	isns_function_name(uint32_t);
++
++extern isns_source_t *	isns_simple_get_source(isns_simple_t *);
++
++extern int		isns_process_registration(isns_server_t *, isns_simple_t *, isns_simple_t **);
++extern int		isns_process_query(isns_server_t *, isns_simple_t *, isns_simple_t **);
++extern int		isns_process_getnext(isns_server_t *, isns_simple_t *, isns_simple_t **);
++extern int		isns_process_deregistration(isns_server_t *, isns_simple_t *, isns_simple_t **);
++extern int		isns_process_scn_register(isns_server_t *, isns_simple_t *, isns_simple_t **);
++extern int		isns_process_scn_deregistration(isns_server_t *, isns_simple_t *, isns_simple_t **);
++extern int		isns_process_dd_registration(isns_server_t *, isns_simple_t *, isns_simple_t **);
++extern int		isns_process_dd_deregistration(isns_server_t *, isns_simple_t *, isns_simple_t **);
++extern int		isns_process_esi(isns_server_t *, isns_simple_t *, isns_simple_t **);
++extern int		isns_process_scn(isns_server_t *, isns_simple_t *, isns_simple_t **);
++
++/*
++ * Inline functions for message queues.
++ */
++static inline void
++isns_message_queue_init(isns_message_queue_t *q)
++{
++	isns_list_init(&q->imq_list);
++	q->imq_count = 0;
++}
++
++static inline isns_message_t *
++isns_message_queue_head(const isns_message_queue_t *q)
++{
++	isns_list_t	*pos = q->imq_list.next;
++
++	if (pos == &q->imq_list)
++		return NULL;
++	return isns_list_item(isns_message_t, im_list, pos);
++}
++
++static inline void
++isns_message_queue_append(isns_message_queue_t *q, isns_message_t *msg)
++{
++	isns_assert(msg->im_queue == NULL);
++	isns_list_append(&q->imq_list, &msg->im_list);
++	q->imq_count++;
++
++	msg->im_queue = q;
++	msg->im_users++;
++}
++
++static inline isns_message_t *
++isns_message_queue_remove(isns_message_queue_t *q, isns_message_t *msg)
++{
++	isns_assert(msg->im_queue == q);
++	isns_list_del(&msg->im_list);
++	msg->im_queue = NULL;
++	q->imq_count--;
++
++	return msg;
++}
++
++static inline isns_message_t *
++isns_message_unlink(isns_message_t *msg)
++{
++	if (msg->im_queue)
++		return isns_message_queue_remove(msg->im_queue, msg);
++	return NULL;
++}
++
++static inline isns_message_t *
++isns_message_dequeue(isns_message_queue_t *q)
++{
++	isns_message_t	*msg;
++
++	if ((msg = isns_message_queue_head(q)) != NULL) {
++		isns_list_del(&msg->im_list);
++		msg->im_queue = NULL;
++		q->imq_count--;
++	}
++	return msg;
++}
++
++/*
++ * Iterator for looping over all messages in a queue
++ */
++static inline void
++isns_message_queue_begin(isns_message_queue_t *q, isns_list_t **pos)
++{
++	*pos = q->imq_list.next;
++}
++
++static inline isns_message_t *
++isns_message_queue_next(isns_message_queue_t *q, isns_list_t **pos)
++{
++	isns_list_t *next = *pos;
++
++	if (next == &q->imq_list)
++		return NULL;
++	*pos = next->next;
++	return isns_list_item(isns_message_t, im_list, next);
++}
++
++#define isns_message_queue_foreach(q, pos, item) \
++	for (isns_message_queue_begin(q, &pos); \
++	     (item = isns_message_queue_next(q, &pos)) != NULL; \
++	    )
++
++#endif /* ISNS_MESSAGE_H */
+diff --git a/utils/open-isns/objects.c b/utils/open-isns/objects.c
+new file mode 100644
+index 0000000..1504026
+--- /dev/null
++++ b/utils/open-isns/objects.c
+@@ -0,0 +1,1320 @@
++/*
++ * iSNS object model
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include "isns.h"
++#include "objects.h"
++#include "source.h"
++#include "vendor.h"
++#include "attrs.h"
++#include "util.h"
++
++/* For relationship stuff - should go */
++#include "db.h"
++
++static isns_object_template_t *	isns_object_templates[] = {
++	&isns_entity_template,
++	&isns_portal_template,
++	&isns_iscsi_node_template,
++	&isns_fc_port_template,
++	&isns_fc_node_template,
++	&isns_iscsi_pg_template,
++	&isns_dd_template,
++	&isns_ddset_template,
++
++	/* vendor-specific templates */
++	&isns_policy_template,
++
++	NULL
++};
++
++/*
++ * Quick lookup of (key) tag to template
++ */
++#define MAX_QUICK_TAG		2100
++static isns_object_template_t *	isns_object_template_key_map[MAX_QUICK_TAG];
++static isns_object_template_t *	isns_object_template_any_map[MAX_QUICK_TAG];
++static isns_object_template_t *	isns_object_template_idx_map[MAX_QUICK_TAG];
++static int			isns_object_maps_inizialized = 0;
++
++
++static void
++__isns_object_maps_init(void)
++{
++	isns_object_template_t *tmpl;
++	uint32_t	i, j, tag;
++
++	isns_object_maps_inizialized = 1;
++
++	for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) {
++		if (tmpl->iot_vendor_specific)
++			continue;
++
++		tag = tmpl->iot_keys[0];
++		isns_assert(tag < MAX_QUICK_TAG);
++		isns_object_template_key_map[tag] = tmpl;
++
++		for (j = 0; j < tmpl->iot_num_attrs; ++j) {
++			tag = tmpl->iot_attrs[j];
++			isns_assert(tag < MAX_QUICK_TAG);
++			isns_object_template_any_map[tag] = tmpl;
++		}
++
++		if ((tag = tmpl->iot_index) != 0)
++			isns_object_template_idx_map[tag] = tmpl;
++	}
++}
++
++static void
++isns_object_maps_init(void)
++{
++	if (!isns_object_maps_inizialized)
++		__isns_object_maps_init();
++}
++
++/*
++ * Based on a given key attribute, find the corresponding
++ * object type.
++ */
++isns_object_template_t *
++isns_object_template_find(uint32_t key_tag)
++{
++	isns_object_template_t *tmpl;
++	unsigned int	i;
++
++	isns_object_maps_init();
++	if (key_tag < MAX_QUICK_TAG)
++		return isns_object_template_key_map[key_tag];
++
++	for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) {
++		if (tmpl->iot_keys[0] == key_tag)
++			return tmpl;
++	}
++
++	return NULL;
++}
++
++/*
++ * Given a set of attributes, find the corresponding
++ * object type.
++ * Any attributes in the list in *addition to* the keys
++ * attributes are ignored.
++ */
++isns_object_template_t *
++isns_object_template_for_key_attrs(const isns_attr_list_t *attrs)
++{
++	isns_object_template_t *tmpl;
++	const isns_attr_t *attr;
++	unsigned int	i;
++
++	if (attrs->ial_count == 0)
++		return NULL;
++	attr = attrs->ial_data[0];
++
++	tmpl = isns_object_template_find(attr->ia_tag_id);
++	if (tmpl == NULL)
++		return NULL;
++
++	/*
++	 * 5.6.4.
++	 *
++	 * Some objects are keyed by more than one object key attribute
++	 * value. For example, the Portal object is keyed by attribute
++	 * tags 16 and 17.  When describing an object keyed by more than one
++	 * key attribute, every object key attribute of that object MUST be
++	 * listed sequentially by tag value in the message before non-key
++	 * attributes of that object and key attributes of the next object.
++	 * A group of key attributes of this kind is treated as a single
++	 * logical key attribute when identifying an object.
++	 */
++	for (i = 1; i < tmpl->iot_num_keys; ++i) {
++		attr = attrs->ial_data[i];
++
++		if (attr->ia_tag_id != tmpl->iot_keys[i])
++			return NULL;
++	}
++
++	return tmpl;
++}
++
++isns_object_template_t *
++isns_object_template_for_tag(uint32_t tag)
++{
++	isns_object_template_t *tmpl;
++	unsigned int	i, j;
++
++	isns_object_maps_init();
++	if (tag < MAX_QUICK_TAG)
++		return isns_object_template_any_map[tag];
++
++	for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) {
++		for (j = 0; j < tmpl->iot_num_attrs; ++j) {
++			if (tmpl->iot_attrs[j] == tag)
++				return tmpl;
++		}
++	}
++
++	return NULL;
++}
++
++isns_object_template_t *
++isns_object_template_for_index_tag(uint32_t tag)
++{
++	isns_object_maps_init();
++	if (tag >= MAX_QUICK_TAG)
++		return NULL;
++
++	return isns_object_template_idx_map[tag];
++}
++
++isns_object_template_t *
++isns_object_template_by_name(const char *name)
++{
++	isns_object_template_t **pp, *tmpl;
++
++	pp = isns_object_templates;
++	while ((tmpl = *pp++) != NULL) {
++		if (!strcasecmp(tmpl->iot_name, name))
++			return tmpl;
++	}
++	return NULL;
++}
++
++const char *
++isns_object_template_name(isns_object_template_t *tmpl)
++{
++	if (!tmpl)
++		return NULL;
++	return tmpl->iot_name;
++}
++
++/*
++ * Notify any listeners that the object has changed,
++ * and mark it dirty.
++ * dd_or_dds is used for DD_MEMBER_ADDED and
++ * DD_MEMBER_REMOVED events, and refers to the
++ * domain or domain set the object was added to or
++ * removed from.
++ */
++void
++isns_mark_object(isns_object_t *obj, unsigned int how)
++{
++	obj->ie_flags |= ISNS_OBJECT_DIRTY;
++	obj->ie_mtime = time(NULL);
++	obj->ie_scn_bits |= (1 << how);
++	isns_object_event(obj, 0, NULL);
++}
++
++static void
++__isns_mark_object(isns_object_t *obj)
++{
++	obj->ie_flags |= ISNS_OBJECT_DIRTY;
++	obj->ie_mtime = time(NULL);
++}
++
++/*
++ * Create an object given its object template
++ */
++isns_object_t *
++isns_create_object(isns_object_template_t *tmpl,
++		const isns_attr_list_t *attrs,
++		isns_object_t *parent)
++{
++	isns_object_t	*obj;
++	unsigned int	i;
++
++	/* Enforce containment rules. */
++	if (parent)
++		isns_assert(tmpl->iot_container == parent->ie_template);
++
++#ifdef notdef
++	/* This check is somewhat costly: */
++	if (attrs && tmpl != isns_object_template_for_key_attrs(attrs))
++		return NULL;
++#endif
++
++	obj = isns_calloc(1, sizeof(*obj));
++
++	obj->ie_users = 1;
++	obj->ie_template = tmpl;
++	isns_attr_list_init(&obj->ie_attrs);
++
++	if (parent)
++		isns_object_attach(obj, parent);
++
++	if (attrs == NULL) {
++		/* Make sure that all key attrs are instantiated
++		 * and in sequence. */
++		for (i = 0; i < tmpl->iot_num_keys; ++i)
++			isns_attr_list_append_nil(&obj->ie_attrs,
++					tmpl->iot_keys[i]);
++	} else {
++		/* We rely on the caller to ensure that
++		 * attributes are in proper sequence. */
++		isns_attr_list_copy(&obj->ie_attrs, attrs);
++	}
++
++	/* Just mark it dirty, but do not schedule a
++	 * SCN event. */
++	__isns_mark_object(obj);
++
++	return obj;
++}
++
++/*
++ * Obtain an additional reference on the object
++ */
++isns_object_t *
++isns_object_get(isns_object_t *obj)
++{
++	if (obj) {
++		isns_assert(obj->ie_users);
++		obj->ie_users++;
++	}
++	return obj;
++}
++
++/*
++ * Release a reference on the object
++ */
++void
++isns_object_release(isns_object_t *obj)
++{
++	unsigned int	i;
++	isns_object_t	*child;
++
++	if (!obj)
++		return;
++
++	isns_assert(obj->ie_users);
++	if (--(obj)->ie_users != 0)
++		return;
++
++	/* Must not have any live references to it */
++	isns_assert(obj->ie_references == 0);
++
++	/* Must be detached from parent */
++	isns_assert(obj->ie_container == NULL);
++
++	/* Release all children. We explicitly clear
++	 * ie_container because the destructor
++	 * checks for this (in order to catch
++	 * refcounting bugs) */
++	for (i = 0; i < obj->ie_children.iol_count; ++i) {
++		child = obj->ie_children.iol_data[i];
++		child->ie_container = NULL;
++	}
++	isns_object_list_destroy(&obj->ie_children);
++
++	isns_attr_list_destroy(&obj->ie_attrs);
++
++	isns_bitvector_free(obj->ie_membership);
++	isns_free(obj);
++}
++
++/*
++ * Get the topmost container (ie Network Entity)
++ * for the given object
++ */
++isns_object_t *
++isns_object_get_entity(isns_object_t *obj)
++{
++	if (obj == NULL)
++		return NULL;
++	while (obj->ie_container)
++		obj = obj->ie_container;
++	if (!ISNS_IS_ENTITY(obj))
++		return NULL;
++	return obj;
++}
++
++int
++isns_object_contains(const isns_object_t *ancestor,
++		const isns_object_t *descendant)
++{
++	while (descendant) {
++		if (descendant == ancestor)
++			return 1;
++		descendant = descendant->ie_container;
++	}
++	return 0;
++}
++
++/*
++ * Get all children of the specified type
++ */
++void
++isns_object_get_descendants(const isns_object_t *obj,
++		isns_object_template_t *tmpl,
++		isns_object_list_t *result)
++{
++	isns_object_t	*child;
++	unsigned int	i;
++
++	for (i = 0; i < obj->ie_children.iol_count; ++i) {
++		child = obj->ie_children.iol_data[i];
++		if (!tmpl || child->ie_template == tmpl)
++			isns_object_list_append(result, child);
++	}
++}
++
++/*
++ * Attach an object to a new container
++ */
++int
++isns_object_attach(isns_object_t *obj, isns_object_t *parent)
++{
++	isns_assert(obj->ie_container == NULL);
++
++	if (parent) {
++		/* Copy the owner (ie source) from the parent
++		 * object.
++		 * Make sure the parent object type is a valid
++		 * container for this object.
++		 */
++		if (parent->ie_template != obj->ie_template->iot_container) {
++			isns_error("You are not allowed to add a %s object "
++				   "to a %s!\n",
++				   obj->ie_template->iot_name,
++				   parent->ie_template->iot_name);
++			return 0;
++		}
++		obj->ie_flags = parent->ie_flags & ISNS_OBJECT_PRIVATE;
++		isns_object_list_append(&parent->ie_children, obj);
++	}
++	obj->ie_container = parent;
++	return 1;
++}
++
++int
++isns_object_is_valid_container(const isns_object_t *container,
++		isns_object_template_t *child_type)
++{
++	return child_type->iot_container == container->ie_template;
++}
++
++/*
++ * Detach an object from its container
++ */
++int
++isns_object_detach(isns_object_t *obj)
++{
++	isns_object_t	*parent;
++
++	/* Detach from parent */
++	if ((parent = obj->ie_container) != NULL) {
++		int	removed;
++
++		obj->ie_container = NULL;
++		removed = isns_object_list_remove(
++				&parent->ie_children, obj);
++
++		isns_assert(removed != 0);
++	}
++
++	return 0;
++}
++
++/*
++ * Check the type of an object
++ */
++int
++isns_object_is(const isns_object_t *obj,
++		isns_object_template_t *tmpl)
++{
++	return obj->ie_template == tmpl;
++}
++
++int
++isns_object_is_iscsi_node(const isns_object_t *obj)
++{
++	return ISNS_IS_ISCSI_NODE(obj);
++}
++
++int
++isns_object_is_fc_port(const isns_object_t *obj)
++{
++	return ISNS_IS_FC_PORT(obj);
++}
++
++int
++isns_object_is_fc_node(const isns_object_t *obj)
++{
++	return ISNS_IS_FC_NODE(obj);
++}
++
++int
++isns_object_is_portal(const isns_object_t *obj)
++{
++	return ISNS_IS_PORTAL(obj);
++}
++
++int
++isns_object_is_pg(const isns_object_t *obj)
++{
++	return ISNS_IS_PG(obj);
++}
++
++int
++isns_object_is_policy(const isns_object_t *obj)
++{
++	return ISNS_IS_POLICY(obj);
++}
++
++/*
++ * Match an object against a list of attributes.
++ */
++int
++isns_object_match(const isns_object_t *obj,
++		const isns_attr_list_t *attrs)
++{
++	isns_object_template_t *tmpl = obj->ie_template;
++	isns_attr_t	*self, *match;
++	unsigned int	i, j, from = 0;
++	uint32_t	tag;
++
++	/* Fast path: try to compare in-order */
++	while (from < attrs->ial_count) {
++		match = attrs->ial_data[from];
++		self = obj->ie_attrs.ial_data[from];
++
++		if (match->ia_tag_id != self->ia_tag_id)
++			goto slow_path;
++
++		if (!isns_attr_match(self, match))
++			return 0;
++
++		from++;
++	}
++
++	return 1;
++
++slow_path:
++	for (i = from; i < attrs->ial_count; ++i) {
++		isns_attr_t *found = NULL;
++
++		match = attrs->ial_data[i];
++
++		/*
++		 * 5.6.5.2
++		 * A Message Key with zero-length TLV(s) is scoped to
++		 * every object of the type indicated by the zero-length
++		 * TLV(s)
++		 */
++		if (match->ia_value.iv_type == &isns_attr_type_nil) {
++			tag = match->ia_tag_id;
++			if (isns_object_attr_valid(tmpl, tag))
++				continue;
++			return 0;
++		}
++
++		for (j = from; j < obj->ie_attrs.ial_count; ++j) {
++			self = obj->ie_attrs.ial_data[j];
++
++			if (match->ia_tag_id == self->ia_tag_id) {
++				found = self;
++				break;
++			}
++		}
++
++		if (found == NULL)
++			return 0;
++
++		if (!isns_attr_match(self, match))
++			return 0;
++	}
++	return 1;
++}
++
++/*
++ * Find descendant object matching the given key
++ */
++isns_object_t *
++isns_object_find_descendant(isns_object_t *obj, const isns_attr_list_t *keys)
++{
++	isns_object_list_t list = ISNS_OBJECT_LIST_INIT;
++	isns_object_t	*found;
++
++	if (!isns_object_find_descendants(obj, NULL, keys, &list))
++		return NULL;
++
++	found = isns_object_get(list.iol_data[0]);
++	isns_object_list_destroy(&list);
++
++	return found;
++}
++
++int
++isns_object_find_descendants(isns_object_t *obj,
++		isns_object_template_t *tmpl,
++		const isns_attr_list_t *keys,
++		isns_object_list_t *result)
++{
++	isns_object_t	*child;
++	unsigned int	i;
++
++	if ((tmpl == NULL || tmpl == obj->ie_template)
++	 && (keys == NULL || isns_object_match(obj, keys)))
++		isns_object_list_append(result, obj);
++
++	for (i = 0; i < obj->ie_children.iol_count; ++i) {
++		child = obj->ie_children.iol_data[i];
++		isns_object_find_descendants(child, tmpl, keys, result);
++	}
++
++	return result->iol_count;
++}
++
++/*
++ * Return the object's modification time stamp
++ */
++time_t
++isns_object_last_modified(const isns_object_t *obj)
++{
++	return obj->ie_mtime;
++}
++
++/*
++ * Set the SCN bitmap
++ */
++void
++isns_object_set_scn_mask(isns_object_t *obj, uint32_t bitmap)
++{
++	obj->ie_scn_mask = bitmap;
++	__isns_mark_object(obj);
++}
++
++/*
++ * Debugging utility: print the object
++ */
++void
++isns_object_print(isns_object_t *obj, isns_print_fn_t *fn)
++{
++	isns_attr_list_print(&obj->ie_attrs, fn);
++}
++
++/*
++ * Return a string representing the object state
++ */
++const char *
++isns_object_state_string(unsigned int state)
++{
++	switch (state) {
++	case ISNS_OBJECT_STATE_LARVAL:
++		return "larval";
++	case ISNS_OBJECT_STATE_MATURE:
++		return "mature";
++	case ISNS_OBJECT_STATE_LIMBO:
++		return "limbo";
++	case ISNS_OBJECT_STATE_DEAD:
++		return "dead";
++	}
++	return "UNKNOWN";
++}
++
++/*
++ * This is needed when deregistering an object.
++ * Remove all attributes except the key and index attrs.
++ */
++void
++isns_object_prune_attrs(isns_object_t *obj)
++{
++	isns_object_template_t *tmpl = obj->ie_template;
++	uint32_t	tags[16];
++	unsigned int	i;
++
++	isns_assert(tmpl->iot_num_keys + 1 <= 16);
++	for (i = 0; i < tmpl->iot_num_keys; ++i)
++		tags[i] = tmpl->iot_keys[i];
++	if (tmpl->iot_index)
++		tags[i++] = tmpl->iot_index;
++	isns_attr_list_prune(&obj->ie_attrs, tags, i);
++}
++
++/*
++ * Convenience functions
++ */
++
++/*
++ * Create a portal object.
++ * For now, always assume TCP.
++ */
++isns_object_t *
++isns_create_portal(const isns_portal_info_t *info,
++		isns_object_t *parent)
++{
++	isns_object_t	*obj;
++
++	obj = isns_create_object(&isns_portal_template, NULL, parent);
++	isns_portal_to_object(info,
++			ISNS_TAG_PORTAL_IP_ADDRESS,
++			ISNS_TAG_PORTAL_TCP_UDP_PORT,
++			obj);
++	return obj;
++}
++
++/*
++ * Extract all key attrs and place them
++ * in the attribute list.
++ */
++int
++isns_object_extract_keys(const isns_object_t *obj,
++		isns_attr_list_t *list)
++{
++	isns_object_template_t *tmpl = obj->ie_template;
++	const isns_attr_list_t *src = &obj->ie_attrs;
++	unsigned int	i;
++
++	for (i = 0; i < tmpl->iot_num_keys; ++i) {
++		isns_attr_t	*attr;
++
++		if (!isns_attr_list_get_attr(src, tmpl->iot_keys[i], &attr))
++			return 0;
++		isns_attr_list_append_attr(list, attr);
++	}
++
++	return 1;
++}
++
++/*
++ * Extract all attributes we are permitted to overwrite and place them
++ * in the attribute list.
++ */
++int
++isns_object_extract_writable(const isns_object_t *obj,
++		isns_attr_list_t *list)
++{
++	const isns_attr_list_t *src = &obj->ie_attrs;
++	unsigned int	i;
++
++	for (i = 0; i < src->ial_count; ++i) {
++		isns_attr_t	*attr = src->ial_data[i];
++
++		if (attr->ia_tag->it_readonly)
++			continue;
++		isns_attr_list_append_attr(list, attr);
++	}
++
++	return 1;
++}
++
++/*
++ * Extract all attrs and place them
++ * in the attribute list. We copy the attributes
++ * as they appear inside the object; which allows
++ * duplicate attributes (eg inside a discovery domain).
++ */
++int
++isns_object_extract_all(const isns_object_t *obj, isns_attr_list_t *list)
++{
++	isns_attr_list_append_list(list, &obj->ie_attrs);
++	return 1;
++}
++
++/*
++ * Check if the given object is valid
++ */
++int
++isns_object_attr_valid(isns_object_template_t *tmpl, uint32_t tag)
++{
++	const uint32_t	*attr_tags = tmpl->iot_attrs;
++	unsigned int	i;
++
++	for (i = 0; i < tmpl->iot_num_attrs; ++i) {
++		if (*attr_tags == tag)
++			return 1;
++		++attr_tags;
++	}
++	return 0;
++}
++
++/*
++ * Set an object attribute
++ */
++static int
++__isns_object_set_attr(isns_object_t *obj, uint32_t tag,
++		const isns_attr_type_t *type,
++		const isns_value_t *value)
++{
++	const isns_tag_type_t *tag_type;
++
++	if (!isns_object_attr_valid(obj->ie_template, tag))
++		return 0;
++
++	tag_type = isns_tag_type_by_id(tag);
++	if (type != &isns_attr_type_nil
++	 && type != tag_type->it_type) {
++		isns_warning("application bug: cannot set attr %s(id=%u, "
++			"type=%s) to a value of type %s\n",
++			tag_type->it_name, tag,
++			tag_type->it_type->it_name,
++			type->it_name);
++		return 0;
++	}
++
++	isns_attr_list_update_value(&obj->ie_attrs,
++			tag, tag_type, value);
++
++	/* Timestamp updates should just be written out, but we
++	 * do not want to trigger SCN messages and such. */
++	if (tag != ISNS_TAG_TIMESTAMP)
++		isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED);
++	else
++		__isns_mark_object(obj);
++	return 1;
++}
++
++/*
++ * Copy an attribute to the object
++ */
++int
++isns_object_set_attr(isns_object_t *obj, isns_attr_t *attr)
++{
++	isns_attr_list_t *list = &obj->ie_attrs;
++	uint32_t	tag = attr->ia_tag_id;
++
++	/* If this attribute exists within the object,
++	 * and it cannot occur multiple times, replace it. */
++	if (!attr->ia_tag->it_multiple
++	 && isns_attr_list_replace_attr(list, attr))
++		goto done;
++
++	/* It doesn't exist; make sure it's a valid
++	 * attribute. */
++	if (!isns_object_attr_valid(obj->ie_template, tag))
++		return 0;
++
++	isns_attr_list_append_attr(list, attr);
++
++done:
++	isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED);
++	return 1;
++}
++
++int
++isns_object_set_attrlist(isns_object_t *obj, const isns_attr_list_t *attrs)
++{
++	unsigned int	i;
++
++	for (i = 0; i < attrs->ial_count; ++i) {
++		isns_attr_t	*attr = attrs->ial_data[i];
++		if (!isns_object_set_attr(obj, attr))
++			return 0;
++	}
++	isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED);
++	return 1;
++}
++
++/*
++ * Untyped version of isns_object_set.
++ * Any type checking must be done by the caller;
++ * failure to do so will result in the end of the world.
++ */
++int
++isns_object_set_value(isns_object_t *obj, uint32_t tag, const void *data)
++{
++	return isns_attr_list_update(&obj->ie_attrs, tag, data);
++}
++
++/*
++ * Typed versions of isns_object_set
++ */
++int
++isns_object_set_nil(isns_object_t *obj, uint32_t tag)
++{
++	return __isns_object_set_attr(obj, tag,
++			&isns_attr_type_nil, NULL);
++}
++
++int
++isns_object_set_string(isns_object_t *obj, uint32_t tag,
++		const char *value)
++{
++	isns_value_t var = ISNS_VALUE_INIT(string, (char *) value);
++	int	rc;
++
++	rc = __isns_object_set_attr(obj, tag,
++			&isns_attr_type_string, &var);
++	return rc;
++}
++
++int
++isns_object_set_uint32(isns_object_t *obj, uint32_t tag,
++		uint32_t value)
++{
++	isns_value_t var = ISNS_VALUE_INIT(uint32, value);
++
++	return __isns_object_set_attr(obj, tag,
++			&isns_attr_type_uint32, &var);
++}
++
++int
++isns_object_set_uint64(isns_object_t *obj,	
++				uint32_t tag,
++				uint64_t value)
++{
++	isns_value_t var = ISNS_VALUE_INIT(uint64, value);
++
++	return __isns_object_set_attr(obj, tag,
++			&isns_attr_type_uint64, &var);
++}
++
++int
++isns_object_set_ipaddr(isns_object_t *obj, uint32_t tag,
++		const struct in6_addr *value)
++{
++	isns_value_t var = ISNS_VALUE_INIT(ipaddr, *value);
++
++	return __isns_object_set_attr(obj, tag,
++			&isns_attr_type_ipaddr, &var);
++}
++
++/*
++ * Query object attributes
++ */
++int
++isns_object_get_attr(const isns_object_t *obj, uint32_t tag,
++		isns_attr_t **result)
++{
++	return isns_attr_list_get_attr(&obj->ie_attrs, tag, result);
++}
++
++int
++isns_object_get_attrlist(isns_object_t *obj,
++		isns_attr_list_t *result,
++		const isns_attr_list_t *req_attrs)
++{
++	isns_attr_list_t *attrs = &obj->ie_attrs;
++	isns_attr_t	*attr;
++	unsigned int	i;
++
++	if (req_attrs == NULL) {
++		/* Retrieve all attributes */
++		isns_attr_list_append_list(result, attrs);
++	} else {
++		for (i = 0; i < req_attrs->ial_count; ++i) {
++			uint32_t tag = req_attrs->ial_data[i]->ia_tag_id;
++
++			if (tag == obj->ie_template->iot_next_index) {
++				/* FIXME: for now, we fake this value.
++				 * We need the DB object at this point
++				 * to find out what the next unused
++				 * index is.
++				 */
++				isns_attr_list_append_uint32(result,
++						tag, 0);
++			} else
++			if (isns_attr_list_get_attr(attrs, tag, &attr))
++				isns_attr_list_append_attr(result, attr);
++		}
++	}
++	return 1;
++}
++
++int
++isns_object_get_key_attrs(isns_object_t *obj,
++			isns_attr_list_t *result)
++{
++	isns_object_template_t *tmpl = obj->ie_template;
++	isns_attr_list_t *attrs = &obj->ie_attrs;
++	isns_attr_t	*attr;
++	unsigned int	i;
++
++	for (i = 0; i < tmpl->iot_num_keys; ++i) {
++		uint32_t	tag = tmpl->iot_keys[i];
++
++		if (!isns_attr_list_get_attr(attrs, tag, &attr)) {
++			isns_error("%s: %s object is missing key attr %u\n",
++					__FUNCTION__,
++					tmpl->iot_name,
++					tag);
++			return 0;
++		}
++		isns_attr_list_append_attr(result, attr);
++	}
++	return 1;
++}
++
++int
++isns_object_get_string(const isns_object_t *obj, uint32_t tag,
++			const char **result)
++{
++	isns_attr_t	*attr;
++
++	if (!isns_object_get_attr(obj, tag, &attr)
++	 || !ISNS_ATTR_IS_STRING(attr))
++		return 0;
++
++	*result = attr->ia_value.iv_string;
++	return 1;
++}
++
++int
++isns_object_get_ipaddr(const isns_object_t *obj, uint32_t tag,
++			struct in6_addr *result)
++{
++	isns_attr_t	*attr;
++
++	if (!isns_object_get_attr(obj, tag, &attr)
++	 || !ISNS_ATTR_IS_IPADDR(attr))
++		return 0;
++
++	*result = attr->ia_value.iv_ipaddr;
++	return 1;
++}
++
++int
++isns_object_get_uint32(const isns_object_t *obj, uint32_t tag,
++			uint32_t *result)
++{
++	isns_attr_t	*attr;
++
++	if (!isns_object_get_attr(obj, tag, &attr)
++	 || !ISNS_ATTR_IS_UINT32(attr))
++		return 0;
++
++	*result = attr->ia_value.iv_uint32;
++	return 1;
++}
++
++int
++isns_object_get_uint64(const isns_object_t *obj, uint32_t tag,
++			uint64_t *result)
++{
++	isns_attr_t	*attr;
++
++	if (!isns_object_get_attr(obj, tag, &attr)
++	 || !ISNS_ATTR_IS_UINT64(attr))
++		return 0;
++
++	*result = attr->ia_value.iv_uint64;
++	return 1;
++}
++
++int
++isns_object_get_opaque(const isns_object_t *obj, uint32_t tag,
++			const void **ptr, size_t *len)
++{
++	isns_attr_t	*attr;
++
++	if (!isns_object_get_attr(obj, tag, &attr)
++	 || !ISNS_ATTR_IS_OPAQUE(attr))
++		return 0;
++
++	*ptr = attr->ia_value.iv_opaque.ptr;
++	*len = attr->ia_value.iv_opaque.len;
++	return 1;
++}
++
++int
++isns_object_delete_attr(isns_object_t *obj, uint32_t tag)
++{
++	return isns_attr_list_remove_tag(&obj->ie_attrs, tag);
++}
++
++int
++isns_object_remove_member(isns_object_t *obj,
++		const isns_attr_t *attr,
++		const uint32_t *subordinate_tags)
++{
++	return isns_attr_list_remove_member(&obj->ie_attrs,
++			attr, subordinate_tags);
++}
++
++/*
++ * Object list functions
++ */
++void
++isns_object_list_init(isns_object_list_t *list)
++{
++	memset(list, 0, sizeof(*list));
++}
++
++static inline void
++__isns_object_list_resize(isns_object_list_t *list, unsigned int count)
++{
++	unsigned int	max;
++
++	max = (list->iol_count + 15) & ~15;
++	if (count < max)
++		return;
++
++	count = (count + 15) & ~15;
++	list->iol_data = isns_realloc(list->iol_data, count * sizeof(isns_object_t *));
++	if (!list->iol_data)
++		isns_fatal("Out of memory!\n");
++}
++
++void
++isns_object_list_append(isns_object_list_t *list, isns_object_t *obj)
++{
++	__isns_object_list_resize(list, list->iol_count + 1);
++	list->iol_data[list->iol_count++] = obj;
++	obj->ie_users++;
++}
++
++void
++isns_object_list_append_list(isns_object_list_t *dst,
++			const isns_object_list_t *src)
++{
++	unsigned int	i, j;
++
++	__isns_object_list_resize(dst, dst->iol_count + src->iol_count);
++	j = dst->iol_count;
++	for (i = 0; i < src->iol_count; ++i, ++j) {
++		isns_object_t *obj = src->iol_data[i];
++
++		dst->iol_data[j] = obj;
++		obj->ie_users++;
++	}
++	dst->iol_count = j;
++}
++
++int
++isns_object_list_contains(const isns_object_list_t *list,
++		isns_object_t *obj)
++{
++	unsigned int	i;
++
++	for (i = 0; i < list->iol_count; ++i) {
++		if (obj == list->iol_data[i])
++			return 1;
++	}
++	return 0;
++}
++
++isns_object_t *
++isns_object_list_lookup(const isns_object_list_t *list,
++		isns_object_template_t *tmpl,
++		const isns_attr_list_t *keys)
++{
++	unsigned int	i;
++
++	if (!tmpl && !keys)
++		return NULL;
++
++	if (!tmpl && !(tmpl = isns_object_template_for_key_attrs(keys)))
++		return NULL;
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t	*obj = list->iol_data[i];
++
++		if (obj->ie_template != tmpl)
++			continue;
++		if (keys && !isns_object_match(obj, keys))
++			continue;
++
++		obj->ie_users++;
++		return obj;
++	}
++
++	return NULL;
++}
++
++
++int
++isns_object_list_gang_lookup(const isns_object_list_t *list,
++		isns_object_template_t *tmpl,
++		const isns_attr_list_t *keys,
++		isns_object_list_t *result)
++{
++	unsigned int	i;
++
++	if (!tmpl && !keys)
++		return ISNS_INVALID_QUERY;
++
++	if (!tmpl && !(tmpl = isns_object_template_for_key_attrs(keys)))
++		return ISNS_INVALID_QUERY;
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t	*obj = list->iol_data[i];
++
++		if (obj->ie_template != tmpl)
++			continue;
++		if (keys && !isns_object_match(obj, keys))
++			continue;
++
++		isns_object_list_append(result, obj);
++	}
++
++	return ISNS_SUCCESS;
++}
++
++
++void
++isns_object_list_destroy(isns_object_list_t *list)
++{
++	unsigned int	i;
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t	*obj = list->iol_data[i];
++
++		isns_object_release(obj);
++	}
++
++	isns_free(list->iol_data);
++	memset(list, 0, sizeof(*list));
++}
++
++int
++isns_object_list_remove(isns_object_list_t *list, isns_object_t *tbr)
++{
++	unsigned int	i, last;
++
++	last = list->iol_count - 1;
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t	*obj = list->iol_data[i];
++
++		if (obj == tbr) {
++			list->iol_data[i] = list->iol_data[last];
++			list->iol_count--;
++			isns_object_release(tbr);
++			return 1;
++		}
++	}
++	return 0;
++}
++
++static int
++isns_object_compare_id(const void *pa, const void *pb)
++{
++	const isns_object_t *a = *(const isns_object_t **) pa;
++	const isns_object_t *b = *(const isns_object_t **) pb;
++
++	return (int) a->ie_index - (int) b->ie_index;
++}
++
++void
++isns_object_list_sort(isns_object_list_t *list)
++{
++	if (list->iol_count == 0)
++		return;
++
++	qsort(list->iol_data, list->iol_count,
++			sizeof(void *), isns_object_compare_id);
++}
++
++void
++isns_object_list_uniq(isns_object_list_t *list)
++{
++	isns_object_t	*prev = NULL, *this;
++	unsigned int	i, j;
++
++	isns_object_list_sort(list);
++	for (i = j = 0; i < list->iol_count; i++) {
++		this = list->iol_data[i];
++		if (this != prev)
++			list->iol_data[j++] = this;
++		prev = this;
++	}
++	list->iol_count = j;
++}
++
++void
++isns_object_list_print(const isns_object_list_t *list, isns_print_fn_t *fn)
++{
++	unsigned int	i;
++
++	if (list->iol_count == 0) {
++		fn("(Object list empty)\n");
++		return;
++	}
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t *obj;
++
++		obj = list->iol_data[i];
++		fn("object[%u] = <%s>\n", i,
++				obj->ie_template->iot_name);
++		isns_object_print(obj, fn);
++	}
++}
++
++/*
++ * Handle object references
++ */
++void
++isns_object_reference_set(isns_object_ref_t *ref, isns_object_t *obj)
++{
++	isns_object_t *old;
++
++	if (obj) {
++		isns_assert(obj->ie_users);
++		obj->ie_references++;
++		obj->ie_users++;
++	}
++	if ((old = ref->obj) != NULL) {
++		isns_assert(old->ie_references);
++		old->ie_references--;
++		isns_object_release(old);
++	}
++	ref->obj = obj;
++}
++
++void
++isns_object_reference_drop(isns_object_ref_t *ref)
++{
++	isns_object_reference_set(ref, NULL);
++}
++
++/*
++ * Helper function for portal/object conversion
++ */
++int
++isns_portal_from_object(isns_portal_info_t *portal,
++		uint32_t addr_tag, uint32_t port_tag,
++		const isns_object_t *obj)
++{
++	return isns_portal_from_attr_list(portal,
++			addr_tag, port_tag, &obj->ie_attrs);
++}
++
++int
++isns_portal_to_object(const isns_portal_info_t *portal, 
++		uint32_t addr_tag, uint32_t port_tag,
++		isns_object_t *obj)
++{
++	return isns_portal_to_attr_list(portal,
++			addr_tag, port_tag,
++			&obj->ie_attrs);
++}
++
++/*
++ * Portal
++ */
++static uint32_t	portal_attrs[] = {
++	ISNS_TAG_PORTAL_IP_ADDRESS,
++	ISNS_TAG_PORTAL_TCP_UDP_PORT,
++	ISNS_TAG_PORTAL_SYMBOLIC_NAME,
++	ISNS_TAG_ESI_INTERVAL,
++	ISNS_TAG_ESI_PORT,
++	ISNS_TAG_PORTAL_INDEX,
++	ISNS_TAG_SCN_PORT,
++	ISNS_TAG_PORTAL_NEXT_INDEX,
++	ISNS_TAG_PORTAL_SECURITY_BITMAP,
++	ISNS_TAG_PORTAL_ISAKMP_PHASE_1,
++	ISNS_TAG_PORTAL_ISAKMP_PHASE_2,
++	ISNS_TAG_PORTAL_CERTIFICATE,
++};
++
++static uint32_t	portal_key_attrs[] = {
++	ISNS_TAG_PORTAL_IP_ADDRESS,
++	ISNS_TAG_PORTAL_TCP_UDP_PORT,
++};
++
++isns_object_template_t		isns_portal_template = {
++	.iot_name	= "Portal",
++	.iot_handle	= ISNS_OBJECT_TYPE_PORTAL,
++	.iot_attrs	= portal_attrs,
++	.iot_num_attrs	= array_num_elements(portal_attrs),
++	.iot_keys	= portal_key_attrs,
++	.iot_num_keys	= array_num_elements(portal_key_attrs),
++	.iot_index	= ISNS_TAG_PORTAL_INDEX,
++	.iot_next_index	= ISNS_TAG_PORTAL_NEXT_INDEX,
++	.iot_container	= &isns_entity_template,
++};
+diff --git a/utils/open-isns/objects.h b/utils/open-isns/objects.h
+new file mode 100644
+index 0000000..8cc40c6
+--- /dev/null
++++ b/utils/open-isns/objects.h
+@@ -0,0 +1,168 @@
++/*
++ * iSNS object model
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_OBJECTS_H
++#define ISNS_OBJECTS_H
++
++#include "isns.h"
++#include "attrs.h"
++
++enum isns_object_id {
++	ISNS_OBJECT_TYPE_ENTITY = 1,
++	ISNS_OBJECT_TYPE_NODE,
++	ISNS_OBJECT_TYPE_PORTAL,
++	ISNS_OBJECT_TYPE_PG,
++	ISNS_OBJECT_TYPE_DD,
++	ISNS_OBJECT_TYPE_DDSET,
++	ISNS_OBJECT_TYPE_POLICY,
++	ISNS_OBJECT_TYPE_FC_PORT,
++	ISNS_OBJECT_TYPE_FC_NODE,
++
++	__ISNS_OBJECT_TYPE_MAX
++};
++
++
++struct isns_object_template {
++	const char *		iot_name;
++	unsigned int		iot_handle;	/* internal handle */
++	unsigned int		iot_num_attrs;
++	unsigned int		iot_num_keys;
++	uint32_t *		iot_attrs;
++	uint32_t *		iot_keys;
++	uint32_t		iot_index;
++	uint32_t		iot_next_index;
++
++	isns_object_template_t *iot_container;
++
++	unsigned int		iot_relation_type;
++	isns_relation_t *	(*iot_build_relation)(isns_db_t *,
++					isns_object_t *,
++					const isns_object_list_t *);
++
++	unsigned int		iot_vendor_specific : 1;
++};
++
++struct isns_object {
++	/* There are two kinds of users of an object
++	 *  -	Temporary references that result from the
++	 *	object being examined; being on a list,
++	 *	etc. The main purpose of these references
++	 *	is to make sure the object doesn't go away
++	 *	while being used.
++	 *
++	 *	These are accounted for by ie_users.
++	 *
++	 *  -	Permanent references that result from the
++	 *	object being references by other objects
++	 *	(usually relations) such as a Portal Group,
++	 *	or a Discovery Domain.
++	 *
++	 *	These are accounted for by ie_references.
++	 *
++	 *	The main purpose of these references is to
++	 *	model some of the weirder life cycle states
++	 *	described in RFC 4711.
++	 *
++	 * Every reference via ie_references implies a
++	 * reference via ie_users.
++	 */
++	unsigned int		ie_users;
++	unsigned int		ie_references;
++
++	uint32_t		ie_index;
++
++	unsigned int		ie_state;
++	unsigned int		ie_flags;
++	time_t			ie_mtime;
++
++	uint32_t		ie_scn_mask;	/* Events this node listens for */
++	uint32_t		ie_scn_bits;	/* Current event bits */
++
++	isns_attr_list_t	ie_attrs;
++	isns_object_t *		ie_container;
++	uint32_t		ie_container_idx;
++	isns_object_template_t *ie_template;
++
++	isns_relation_t *	ie_relation;
++	isns_object_list_t	ie_children;
++
++	/* Bit vector describing DD membership */
++	isns_bitvector_t *	ie_membership;
++
++	/* Support for virtual objects */
++	int			(*ie_rebuild)(isns_object_t *, isns_db_t *);
++};
++
++typedef struct isns_object_ref {
++	isns_object_t *		obj;
++} isns_object_ref_t;
++
++enum {
++	ISNS_RELATION_NONE = 0,
++	ISNS_RELATION_PORTAL_GROUP,
++};
++
++struct isns_relation {
++	unsigned int		ir_type;
++	unsigned int		ir_users;
++	isns_object_t *		ir_object;
++	isns_object_ref_t	ir_subordinate[2];
++};
++
++typedef struct isns_relation_soup isns_relation_soup_t;
++
++typedef struct isns_relation_list isns_relation_list_t;
++struct isns_relation_list {
++	unsigned int		irl_count;
++	isns_relation_t **	irl_data;
++};
++#define ISNS_RELATION_LIST_INIT { .irl_count = 0, .irl_data = NULL }
++
++#define ISNS_OBJECT_DIRTY	0x0001
++#define ISNS_OBJECT_PRIVATE	0x0002
++#define ISNS_OBJECT_DEAD	0x0004
++
++enum {
++	ISNS_OBJECT_STATE_LARVAL,
++	ISNS_OBJECT_STATE_MATURE,
++	ISNS_OBJECT_STATE_LIMBO,
++	ISNS_OBJECT_STATE_DEAD,
++};
++
++extern int	isns_object_remove_member(isns_object_t *obj,
++				const isns_attr_t *attr,
++				const uint32_t *subordinate_tags);
++
++extern void	isns_object_reference_set(isns_object_ref_t *ref,
++				isns_object_t *obj);
++extern void	isns_object_reference_drop(isns_object_ref_t *ref);
++
++extern const char *isns_object_state_string(unsigned int);
++
++extern isns_object_template_t *isns_object_template_by_name(const char *);
++extern int	isns_object_is_valid_container(const isns_object_t *,
++				isns_object_template_t *);
++
++extern void	isns_object_set_scn_mask(isns_object_t *, uint32_t);
++
++extern isns_object_t *isns_create_default_domain(void);
++
++/*
++ * Helper macros for object type check
++ */
++#define __ISNS_OBJECT_TYPE_CHECK(obj, type) \
++		((obj)->ie_template == &isns_##type##_template)
++#define ISNS_IS_ENTITY(obj)	__ISNS_OBJECT_TYPE_CHECK(obj, entity)
++#define ISNS_IS_ISCSI_NODE(obj)	__ISNS_OBJECT_TYPE_CHECK(obj, iscsi_node)
++#define ISNS_IS_FC_PORT(obj)	__ISNS_OBJECT_TYPE_CHECK(obj, fc_port)
++#define ISNS_IS_FC_NODE(obj)	__ISNS_OBJECT_TYPE_CHECK(obj, fc_node)
++#define ISNS_IS_PORTAL(obj)	__ISNS_OBJECT_TYPE_CHECK(obj, portal)
++#define ISNS_IS_PG(obj)		__ISNS_OBJECT_TYPE_CHECK(obj, iscsi_pg)
++#define ISNS_IS_POLICY(obj)	__ISNS_OBJECT_TYPE_CHECK(obj, policy)
++#define ISNS_IS_DD(obj)		__ISNS_OBJECT_TYPE_CHECK(obj, dd)
++#define ISNS_IS_DDSET(obj)	__ISNS_OBJECT_TYPE_CHECK(obj, ddset)
++
++#endif /* ISNS_OBJECTS_H */
+diff --git a/utils/open-isns/parser.c b/utils/open-isns/parser.c
+new file mode 100644
+index 0000000..378f2c8
+--- /dev/null
++++ b/utils/open-isns/parser.c
+@@ -0,0 +1,134 @@
++/*
++ * parser.c - simple line based parser
++ *
++ * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <getopt.h>
++#include <stdio.h>
++#include <string.h>
++#include <ctype.h>
++#include <err.h>
++#include "util.h"
++
++/*
++ * By default, the parser will recognize any white space
++ * as "word" separators.
++ * If you need additional separators, you can put them
++ * here.
++ */
++const char *	parser_separators = NULL;
++const char *	parser_punctuation = "=";
++
++char *
++parser_get_next_line(FILE *fp)
++{
++	static char	buffer[8192];
++	unsigned int	n = 0, count = 0;
++	int		c, continuation = 0;
++
++	while (n < sizeof(buffer) - 1) {
++		c = fgetc(fp);
++		if (c == EOF)
++			break;
++
++		count++;
++		if (c == '\r')
++			continue;
++		/* Discard all blanks
++		 * following a backslash-newline
++		 */
++		if (continuation) {
++			if (c == ' ' || c == '\t')
++				continue;
++			continuation = 0;
++		}
++
++		if (c == '\n') {
++			if (n && buffer[n-1] == '\\') {
++				buffer[--n] = '\0';
++				continuation = 1;
++			}
++			while (n && isspace(buffer[n-1]))
++				buffer[--n] = '\0';
++			if (!continuation)
++				break;
++			buffer[n++] = ' ';
++			continue;
++		}
++
++		buffer[n++] = c;
++	}
++
++	if (count == 0)
++		return NULL;
++
++	buffer[n] = '\0';
++	return buffer;
++}
++
++static inline int
++is_separator(char c)
++{
++	if (isspace(c))
++		return 1;
++	return parser_separators && c && strchr(parser_separators, c);
++}
++
++static inline int
++is_punctuation(char c)
++{
++	return parser_punctuation && c && strchr(parser_punctuation, c);
++}
++
++char *
++parser_get_next_word(char **sp)
++{
++	static char buffer[512];
++	char	*s = *sp, *p = buffer;
++
++	while (is_separator(*s))
++		++s;
++
++	if (*s == '\0')
++		goto done;
++
++	if (is_punctuation(*s)) {
++		*p++ = *s++;
++		goto done;
++	}
++
++	while (*s && !is_separator(*s) && !is_punctuation(*s))
++		*p++ = *s++;
++
++done:
++	*p++ = '\0';
++	*sp = s;
++	return buffer[0]? buffer : NULL;
++}
++
++int
++parser_split_line(char *line, unsigned int argsmax, char **argv)
++{
++	unsigned int	argc = 0;
++	char		*s;
++
++	while (argc < argsmax && (s = parser_get_next_word(&line)))
++		argv[argc++] = strdup(s);
++	return argc;
++}
++
++char *
++parser_get_rest_of_line(char **sp)
++{
++	char	*s = *sp, *res = NULL;
++
++	while (is_separator(*s))
++		++s;
++
++	*sp = "";
++	if (*s != '\0')
++		res = s;
++	return res;
++}
+diff --git a/utils/open-isns/paths.h b/utils/open-isns/paths.h
+new file mode 100644
+index 0000000..b54612c
+--- /dev/null
++++ b/utils/open-isns/paths.h
+@@ -0,0 +1,22 @@
++/*
++ * Compile time configuration.
++ * For now, let's keep it simple and ignore autoconf...
++ *
++ * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_CONFIG_H
++#define ISNS_CONFIG_H
++
++#define __OPENISNS_MKVERSION(maj, min)	(((maj) << 8) + (min))
++#define OPENISNS_VERSION		__OPENISNS_MKVERSION(0, 90);
++#define OPENISNS_VERSION_STRING		"0.90"
++
++#define ISNS_ETCDIR			"/etc/isns"
++#define ISNS_RUNDIR			"/var/run"
++#define ISNS_DEFAULT_ISNSD_CONFIG	ISNS_ETCDIR "/isnsd.conf"
++#define ISNS_DEFAULT_ISNSDD_CONFIG	ISNS_ETCDIR "/isnsdd.conf"
++#define ISNS_DEFAULT_ISNSADM_CONFIG	ISNS_ETCDIR "/isnsadm.conf"
++#define ISNS_DEFAULT_LOCAL_REGISTRY	ISNS_RUNDIR "/isns.registry"
++
++#endif /* ISNS_CONFIG_H */
+diff --git a/utils/open-isns/pidfile.c b/utils/open-isns/pidfile.c
+new file mode 100644
+index 0000000..3384373
+--- /dev/null
++++ b/utils/open-isns/pidfile.c
+@@ -0,0 +1,98 @@
++/*
++ * write pidfile
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <fcntl.h>
++#include <stdlib.h>
++#include <string.h>
++#include <signal.h>
++#include <errno.h>
++#include <unistd.h>
++
++#include "util.h"
++
++static void
++__update_pidfile(int fd)
++{
++	char	pidbuf[32];
++
++	snprintf(pidbuf, sizeof(pidbuf), "%u\n", getpid());
++	if (write(fd, pidbuf, strlen(pidbuf)) < 0)
++		isns_fatal("Error writing pid file: %m\n");
++	close(fd);
++}
++
++static pid_t
++__read_pidfile(const char *filename)
++{
++	char	pidbuf[32];
++	FILE	*fp;
++	pid_t	pid = -1;
++
++	fp = fopen(filename, "r");
++	if (fp != NULL) {
++		if (fgets(pidbuf, sizeof(pidbuf), fp))
++			pid = strtoul(pidbuf, NULL, 0);
++		fclose(fp);
++	}
++	return pid;
++}
++
++void
++isns_write_pidfile(const char *filename)
++{
++	int	fd;
++	pid_t	pid;
++
++	fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0644);
++	if (fd >= 0) {
++		__update_pidfile(fd);
++		return;
++	}
++
++	if (errno != EEXIST)
++		isns_fatal("Error creating pid file %s: %m\n",
++			filename);
++
++	/* If the pid file is stale, remove it.
++	 * Not really needed in real life, but
++	 * highly convenient for debugging :) */
++	if ((pid = __read_pidfile(filename)) > 0
++	 && kill(pid, 0) < 0
++	 && errno == ESRCH) {
++		isns_debug_general(
++			"Removing stale PID file %s\n",
++			filename);
++		unlink(filename);
++	}
++
++	/* Try again */
++	fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0644);
++	if (fd < 0)
++		isns_fatal("PID file exists; another daemon "
++		      "seems to be running\n");
++
++	__update_pidfile(fd);
++}
++
++void
++isns_update_pidfile(const char *filename)
++{
++	int	fd;
++
++	fd = open(filename, O_WRONLY);
++	if (fd < 0) {
++		isns_fatal("Error opening pid file %s: %m\n",
++			filename);
++	}
++
++	__update_pidfile(fd);
++}
++
++void
++isns_remove_pidfile(const char *filename)
++{
++	unlink(filename);
++}
+diff --git a/utils/open-isns/pki.c b/utils/open-isns/pki.c
+new file mode 100644
+index 0000000..f3af922
+--- /dev/null
++++ b/utils/open-isns/pki.c
+@@ -0,0 +1,536 @@
++/*
++ * PKI related functions
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <sys/stat.h>
++#include <string.h>
++#include <unistd.h>
++#include <limits.h>
++#include <openssl/pem.h>
++#include <openssl/err.h>
++#include <fcntl.h>
++#include "isns.h"
++#include "security.h"
++#include "util.h"
++#include "config.h"
++
++#ifdef WITH_SECURITY
++
++/* versions prior to 9.6.8 didn't seem to have these */
++#if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906080L
++# define EVP_MD_CTX_init(c)	do { } while (0)
++# define EVP_MD_CTX_cleanup(c)	do { } while (0)
++#endif
++#if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906070L
++# define i2d_DSA_PUBKEY		i2d_DSA_PUBKEY_backwards
++
++static int	i2d_DSA_PUBKEY_backwards(DSA *, unsigned char **);
++#endif
++
++static int	isns_openssl_init = 0;
++
++static int	isns_dsasig_verify(isns_security_t *ctx,
++				isns_principal_t *peer,
++				buf_t *pdu,
++				const struct isns_authblk *);
++static int	isns_dsasig_sign(isns_security_t *ctx,
++				isns_principal_t *peer,
++				buf_t *pdu,
++				struct isns_authblk *);
++static EVP_PKEY *isns_dsasig_load_private_pem(isns_security_t *ctx,
++				const char *filename);
++static EVP_PKEY *isns_dsasig_load_public_pem(isns_security_t *ctx,
++				const char *filename);
++static DSA *	isns_dsa_load_params(const char *);
++
++
++/*
++ * Create a DSA security context
++ */
++isns_security_t *
++isns_create_dsa_context(void)
++{
++	isns_security_t	*ctx;
++
++	if (!isns_openssl_init) {
++		ERR_load_crypto_strings();
++		OpenSSL_add_all_algorithms();
++		OpenSSL_add_all_ciphers();
++		OpenSSL_add_all_digests();
++		isns_openssl_init = 1;
++	}
++
++	ctx = isns_calloc(1, sizeof(*ctx));
++
++	ctx->is_name = "DSA";
++	ctx->is_type = ISNS_AUTH_TYPE_SHA1_DSA;
++	ctx->is_replay_window = isns_config.ic_auth.replay_window;
++	ctx->is_timestamp_jitter = isns_config.ic_auth.timestamp_jitter;
++
++	ctx->is_verify = isns_dsasig_verify;
++	ctx->is_sign = isns_dsasig_sign;
++	ctx->is_load_private = isns_dsasig_load_private_pem;
++	ctx->is_load_public = isns_dsasig_load_public_pem;
++
++	isns_debug_auth("Created DSA authentication context\n");
++	return ctx;
++}
++
++/*
++ * DSA signature generation and verification
++ */
++static void
++isns_message_digest(EVP_MD_CTX *md, const buf_t *pdu,
++		const struct isns_authblk *blk)
++{
++	uint64_t	stamp;
++
++	EVP_DigestUpdate(md, buf_head(pdu), buf_avail(pdu));
++
++	/* The RFC doesn't say which pieces of the
++	 * message should be hashed.
++	 * We make an educated guess.
++	 */
++	stamp = htonll(blk->iab_timestamp);
++	EVP_DigestUpdate(md, &stamp, sizeof(stamp));
++}
++
++static void
++isns_dsasig_report_errors(const char *msg, isns_print_fn_t *fn)
++{
++	unsigned long	code;
++
++	fn("%s - OpenSSL errors follow:\n", msg);
++	while ((code = ERR_get_error()) != 0)
++		fn("> %s: %s\n",
++			ERR_func_error_string(code),
++			ERR_reason_error_string(code));
++}
++
++int
++isns_dsasig_sign(isns_security_t *ctx,
++			isns_principal_t *peer,
++			buf_t *pdu,
++			struct isns_authblk *blk)
++{
++	static unsigned char signature[1024];
++	unsigned int	sig_len = sizeof(signature);
++	EVP_MD_CTX	md_ctx;
++	EVP_PKEY	*pkey;
++	int		err;
++
++	if ((pkey = peer->is_key) == NULL)
++		return 0;
++
++	if (pkey->type != EVP_PKEY_DSA) {
++		isns_debug_message(
++			"Incompatible public key (spi=%s)\n",
++			peer->is_name);
++		return 0;
++	}
++	if (EVP_PKEY_size(pkey) > sizeof(signature)) {
++		isns_error("isns_dsasig_sign: signature buffer too small\n");
++		return 0;
++	}
++	if (pkey->pkey.dsa->priv_key == NULL) {
++		isns_error("isns_dsasig_sign: oops, seems to be a public key\n");
++		return 0;
++	}
++
++	isns_debug_auth("Signing messages with spi=%s, DSA/%u\n",
++			peer->is_name, EVP_PKEY_bits(pkey));
++
++	EVP_MD_CTX_init(&md_ctx);
++	EVP_SignInit(&md_ctx, EVP_dss1());
++	isns_message_digest(&md_ctx, pdu, blk);
++	err = EVP_SignFinal(&md_ctx,
++				signature, &sig_len,
++				pkey);
++	EVP_MD_CTX_cleanup(&md_ctx);
++
++	if (err == 0) {
++		isns_dsasig_report_errors("EVP_SignFinal failed", isns_error);
++		return 0;
++	}
++
++	blk->iab_sig = signature;
++	blk->iab_sig_len = sig_len;
++	return 1;
++}
++
++int
++isns_dsasig_verify(isns_security_t *ctx,
++			isns_principal_t *peer,
++			buf_t *pdu,
++			const struct isns_authblk *blk)
++{
++	EVP_MD_CTX	md_ctx;
++	EVP_PKEY	*pkey;
++	int		err;
++
++	if ((pkey = peer->is_key) == NULL)
++		return 0;
++
++	if (pkey->type != EVP_PKEY_DSA) {
++		isns_debug_message(
++			"Incompatible public key (spi=%s)\n",
++			peer->is_name);
++		return 0;
++	}
++
++	EVP_MD_CTX_init(&md_ctx);
++	EVP_VerifyInit(&md_ctx, EVP_dss1());
++	isns_message_digest(&md_ctx, pdu, blk);
++	err = EVP_VerifyFinal(&md_ctx,
++			blk->iab_sig, blk->iab_sig_len,
++			pkey);
++	EVP_MD_CTX_cleanup(&md_ctx);
++	
++	if (err == 0) {
++		isns_debug_auth("*** Incorrect signature ***\n");
++		return 0;
++	}
++	if (err < 0) {
++		isns_dsasig_report_errors("EVP_VerifyFinal failed", isns_error);
++		return 0;
++	}
++
++	isns_debug_message("Good signature from %s\n",
++			peer->is_name?: "<server>");
++	return 1;
++}
++
++EVP_PKEY *
++isns_dsasig_load_private_pem(isns_security_t *ctx, const char *filename)
++{
++	EVP_PKEY	*pkey;
++	FILE		*fp;
++
++	if (!(fp = fopen(filename, "r"))) {
++		isns_error("Unable to open DSA keyfile %s: %m\n",
++				filename);
++		return 0;
++	}
++
++	pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
++	fclose(fp);
++	return pkey;
++}
++
++EVP_PKEY *
++isns_dsasig_load_public_pem(isns_security_t *ctx, const char *filename)
++{
++	EVP_PKEY	*pkey;
++	FILE		*fp;
++
++	if (!(fp = fopen(filename, "r"))) {
++		isns_error("Unable to open DSA keyfile %s: %m\n",
++				filename);
++		return 0;
++	}
++
++	pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
++	if (pkey == NULL) {
++		isns_dsasig_report_errors("Error loading DSA public key",
++				isns_error);
++	}
++
++	fclose(fp);
++	return pkey;
++}
++
++EVP_PKEY *
++isns_dsa_decode_public(const void *ptr, size_t len)
++{
++	const unsigned char *der = ptr;
++	EVP_PKEY *evp;
++	DSA	*dsa;
++
++	/* Assigning ptr to a temporary variable avoids a silly
++	 * compiled warning about type-punning. */
++	dsa = d2i_DSA_PUBKEY(NULL, &der, len);
++	if (dsa == NULL)
++		return NULL;
++
++	evp = EVP_PKEY_new();
++	EVP_PKEY_assign_DSA(evp, dsa);
++	return evp;
++}
++
++int
++isns_dsa_encode_public(EVP_PKEY *pkey, void **ptr, size_t *len)
++{
++	int	bytes;
++
++	*ptr = NULL;
++	bytes = i2d_DSA_PUBKEY(pkey->pkey.dsa, (unsigned char **) ptr);
++	if (bytes < 0)
++		return 0;
++
++	*len = bytes;
++	return 1;
++}
++
++EVP_PKEY *
++isns_dsa_load_public(const char *name)
++{
++	return isns_dsasig_load_public_pem(NULL, name);
++}
++
++int
++isns_dsa_store_private(const char *name, EVP_PKEY *key)
++{
++	FILE	*fp;
++	int	rv, fd;
++
++	if ((fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
++		isns_error("Cannot save DSA key to %s: %m\n", name);
++		return 0;
++	}
++
++	if (!(fp = fdopen(fd, "w"))) {
++		isns_error("fdopen(%s): %m\n", name);
++		close(fd);
++		return 0;
++	}
++
++	rv = PEM_write_PrivateKey(fp, key, NULL, NULL, 0, 0, NULL);
++	fclose(fp);
++
++	if (rv == 0)
++		isns_dsasig_report_errors("Failed to store private key",
++				isns_error);
++
++	return rv;
++}
++
++int
++isns_dsa_store_public(const char *name, EVP_PKEY *key)
++{
++	FILE	*fp;
++	int	rv;
++
++	if (!(fp = fopen(name, "w"))) {
++		isns_error("Unable to open %s: %m\n", name);
++		return 0;
++	}
++
++	rv = PEM_write_PUBKEY(fp, key);
++	fclose(fp);
++
++	if (rv == 0)
++		isns_dsasig_report_errors("Failed to store public key",
++				isns_error);
++
++	return rv;
++}
++
++
++/*
++ * DSA key generation
++ */
++EVP_PKEY *
++isns_dsa_generate_key(void)
++{
++	EVP_PKEY *pkey;
++	DSA	*dsa = NULL;
++
++	if (!(dsa = isns_dsa_load_params(isns_config.ic_dsa.param_file)))
++		goto failed;
++
++	if (!DSA_generate_key(dsa)) {
++		isns_dsasig_report_errors("Failed to generate DSA key",
++				isns_error);
++		goto failed;
++	}
++
++	pkey = EVP_PKEY_new();
++	EVP_PKEY_assign_DSA(pkey, dsa);
++	return pkey;
++
++failed:
++	if (dsa)
++		DSA_free(dsa);
++	return NULL;
++}
++
++DSA *
++isns_dsa_load_params(const char *filename)
++{
++	FILE	*fp;
++	DSA	*dsa;
++
++	if (!filename) {
++		isns_error("Cannot generate key - no DSA parameter file\n");
++		return NULL;
++	}
++	if (!(fp = fopen(filename, "r"))) {
++		isns_error("Unable to open %s: %m\n", filename);
++		return NULL;
++	}
++
++	dsa = PEM_read_DSAparams(fp, NULL, NULL, NULL);
++	fclose(fp);
++
++	if (dsa == NULL) {
++		isns_dsasig_report_errors("Error loading DSA parameters",
++				isns_error);
++	}
++
++	return dsa;
++}
++
++static void
++isns_dsa_param_gen_callback(int stage, int index, void *dummy)
++{
++	if (stage == 0)
++		write(1, "+", 1);
++	else if (stage == 1)
++		write(1, ".", 1);
++	else if (stage == 2)
++		write(1, "/", 1);
++}
++
++int
++isns_dsa_init_params(const char *filename)
++{
++	FILE	*fp;
++	DSA	*dsa;
++
++	if (access(filename, R_OK) == 0)
++		return 1;
++
++	isns_mkdir_recursive(isns_dirname(filename));
++	if (!(fp = fopen(filename, "w"))) {
++		isns_error("Unable to open %s: %m\n", filename);
++		return 0;
++	}
++
++	isns_notice("Generating DSA parameters; this may take a while\n");
++	dsa = DSA_generate_parameters(1024, NULL, 0,
++			NULL, NULL, isns_dsa_param_gen_callback, NULL);
++	write(1, "\n", 1);
++
++	if (dsa == NULL) {
++		isns_dsasig_report_errors("Error generating DSA parameters",
++				isns_error);
++		fclose(fp);
++		return 0;
++	}
++
++	if (!PEM_write_DSAparams(fp, dsa)) {
++		isns_dsasig_report_errors("Error writing DSA parameters",
++				isns_error);
++		DSA_free(dsa);
++		fclose(fp);
++		return 0;
++	}
++	DSA_free(dsa);
++	fclose(fp);
++	return 1;
++}
++
++/*
++ * Make sure the authentication key is present.
++ */
++int
++isns_dsa_init_key(const char *filename)
++{
++	char	pubkey_path[1024];
++	EVP_PKEY *pkey;
++
++	isns_mkdir_recursive(isns_dirname(filename));
++	snprintf(pubkey_path, sizeof(pubkey_path),
++				"%s.pub", filename);
++	if (access(filename, R_OK) == 0
++	 && access(pubkey_path, R_OK) == 0)
++		return 1;
++
++	if (!(pkey = isns_dsa_generate_key())) {
++		isns_error("Failed to generate AuthKey\n");
++		return 0;
++	}
++
++	if (!isns_dsa_store_private(filename, pkey)) {
++		isns_error("Unable to write private key to %s\n", filename);
++		return 0;
++	}
++	isns_notice("Stored private key in %s\n", filename);
++
++	if (!isns_dsa_store_public(pubkey_path, pkey)) {
++		isns_error("Unable to write public key to %s\n", pubkey_path);
++		return 0;
++	}
++	isns_notice("Stored private key in %s\n", pubkey_path);
++
++	return 1;
++}
++
++/*
++ * Simple keystore - this is a flat directory, with
++ * public key files using the SPI as their name.
++ */
++typedef struct isns_simple_keystore isns_simple_keystore_t;
++struct isns_simple_keystore {
++	isns_keystore_t	sc_base;
++	char *		sc_dirpath;
++};
++
++/*
++ * Load a DSA key from the cert store
++ * In fact, this will load RSA keys as well.
++ */
++static EVP_PKEY *
++__isns_simple_keystore_find(isns_keystore_t *store_base,
++		const char *name, size_t namelen)
++{
++	isns_simple_keystore_t *store = (isns_simple_keystore_t *) store_base;
++	char		pathname[PATH_MAX];
++
++	/* Refuse to open key files with names
++	 * that refer to parent directories */
++	if (memchr(name, '/', namelen) || name[0] == '.')
++		return NULL;
++
++	snprintf(pathname, sizeof(pathname),
++			"%s/%.*s", store->sc_dirpath,
++			(int) namelen, name);
++	if (access(pathname, R_OK) < 0)
++		return NULL;
++	return isns_dsasig_load_public_pem(NULL, pathname);
++}
++
++isns_keystore_t *
++isns_create_simple_keystore(const char *dirname)
++{
++	isns_simple_keystore_t *store;
++
++	store = isns_calloc(1, sizeof(*store));
++	store->sc_base.ic_name = "simple key store";
++	store->sc_base.ic_find = __isns_simple_keystore_find;
++	store->sc_dirpath = isns_strdup(dirname);
++
++	return (isns_keystore_t *) store;
++}
++
++#if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906070L
++#undef i2d_DSA_PUBKEY
++
++int
++i2d_DSA_PUBKEY_backwards(DSA *dsa, unsigned char **ptr)
++{
++	unsigned char *buf;
++	int len;
++
++	len = i2d_DSA_PUBKEY(dsa, NULL);
++	if (len < 0)
++		return 0;
++
++	*ptr = buf = OPENSSL_malloc(len);
++	return i2d_DSA_PUBKEY(dsa, &buf);
++}
++#endif
++
++#endif /* WITH_SECURITY */
+diff --git a/utils/open-isns/policy.c b/utils/open-isns/policy.c
+new file mode 100644
+index 0000000..46de15b
+--- /dev/null
++++ b/utils/open-isns/policy.c
+@@ -0,0 +1,577 @@
++/*
++ * Open-iSNS policy engine
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ *
++ * For now, policy is static. We can make it configurable
++ * later.
++ */
++
++#include <string.h>
++#include "isns.h"
++#include "security.h"
++#include "objects.h"
++#include "message.h"
++#include "util.h"
++
++/*
++   A brief discussion of policy
++
++   For now, a principal's name (ie its SPI string) *must* match
++   the iSNS source name it uses.
++
++   Special care needs to be taken to restrict which principals
++   are permitted to act as a control node. For now, we don't
++   implement control node semantics.
++
++ */
++
++static unsigned int	isns_policy_gen = 0;
++
++/*
++ * Administrative policy (everything allowed,
++ * talks to entity "CONTROL"
++ */
++static isns_policy_t	isns_superhero_powers = {
++	.ip_name		= "administrator",
++	.ip_users		= 1,
++	.ip_gen			= 0,
++
++	.ip_entity		= ISNS_ENTITY_CONTROL,
++	.ip_functions		= ~0,
++	.ip_object_types	= ~0,
++	.ip_node_types		= ~0,
++};
++
++/*
++ * Policy for anon user
++ */
++static isns_policy_t	isns_dweeb_powers = {
++	.ip_name		= "anonymous",
++	.ip_users		= 1,
++	.ip_gen			= 0,
++
++	.ip_functions		= 1 << ISNS_DEVICE_ATTRIBUTE_QUERY,
++	.ip_object_types	= 0,
++	.ip_node_types		= 0,
++};
++
++#define IS_ANON_POLICY(p)	((p) == &isns_dweeb_powers)
++
++/*
++ * These are used when security is turned off.
++ * Essentially the same as superhero, except
++ * no eid specified.
++ */
++static isns_policy_t	isns_flyingpigs_powers = {
++	.ip_name		= "insecure",
++	.ip_users		= 1,
++	.ip_gen			= 0,
++
++	.ip_functions		= ~0,
++	.ip_object_types	= ~0,
++	.ip_node_types		= ~0,
++};
++
++
++isns_policy_t *
++isns_policy_bind(const isns_message_t *msg)
++{
++	isns_policy_t		*policy = NULL;
++	isns_principal_t	*princ = NULL;
++
++	/* When the admin turns off gravity,
++	 * pigs can fly, too. */
++	if (isns_config.ic_security == 0) {
++		policy = &isns_flyingpigs_powers;
++		goto found;
++	}
++
++	/* If the caller is the local root user, s/he can
++	 * do anything. */
++	if (msg->im_creds && msg->im_creds->uid == 0) {
++		policy = &isns_superhero_powers;
++		goto found;
++	}
++
++	/* Tie the SPI given in the auth block to a
++	 * source name.
++	 * For now, the names have to match. Down the road,
++	 * there may be more flexible schemes.
++	 */
++	if ((princ = msg->im_security) != NULL) {
++		if ((policy = princ->is_policy) != NULL)
++			goto found;
++
++		isns_error("Internal error - no policy for "
++				"principal %s!\n",
++				princ->is_name);
++	}
++
++	policy = &isns_dweeb_powers;
++
++found:
++	policy->ip_users++;
++	return policy;
++}
++
++/*
++ * Check whether the call is permitted.
++ * This is particularly useful to prevent rogue
++ * clients from messing with Discovery Domains.
++ */
++int
++isns_policy_validate_function(const isns_policy_t *policy,
++		const isns_message_t *msg)
++{
++	uint32_t function = msg->im_header.i_function;
++	int	rv = 0;
++
++	if (function >= 32) {
++		isns_debug_auth("Bad function code %08x\n", function);
++		return 0;
++	}
++
++	if (!(policy->ip_functions & (1 << function)))
++		goto reject;
++
++	rv = 1;
++
++reject: 
++	isns_debug_auth(":: policy %s function %s (%04x) %s\n",
++			policy->ip_name,
++			isns_function_name(function), function,
++			rv? "permitted" : "DENIED");
++	return rv;
++}
++
++/*
++ * Helper function to validate node names and source names
++ */
++static int
++__validate_node_name(const isns_policy_t *policy, const char *name)
++{
++	const struct string_array *ap;
++	unsigned int i;
++
++	/* Control nodes get to do everything */
++	if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
++		return 1;
++
++	ap = &policy->ip_node_names;
++	for (i = 0; i < ap->count; ++i) {
++		const char *s;
++
++		s = ap->list[i];
++		if (s == NULL)
++			continue;
++		if (isns_source_pattern_match(s, name))
++			return 1;
++	}
++	return 0;
++}
++
++/*
++ * Validate the source of a message
++ */
++int
++isns_policy_validate_source(const isns_policy_t *policy,
++		const isns_source_t *source)
++{
++	const char *src_name = isns_source_name(source);
++	int	rv = 0;
++
++	if (!__validate_node_name(policy, src_name))
++		goto reject;
++
++	rv = 1;
++
++reject:
++	isns_debug_auth(":: policy %s source %s %s\n",
++			policy->ip_name, src_name,
++			rv? "permitted" : "DENIED");
++	return rv;
++}
++
++/*
++ * Check whether the entity name specified by the client
++ * is actually his to use.
++ */
++int
++isns_policy_validate_entity(const isns_policy_t *policy,
++			const char *eid)
++{
++	int	rv = 0, eidlen;
++
++	/* Control nodes get to do everything */
++	if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
++		goto accept;
++
++	/* For anonymous clients, refuse any attempt to
++	 * create an entity */
++	if (IS_ANON_POLICY(policy))
++		goto reject;
++
++	/* If no entity is assigned, this means the client
++	 * is not permitted to specify its own entity name,
++	 * and accept what we assign it.
++	 */
++	if (policy->ip_entity == NULL)
++		goto reject;
++
++	eidlen = strlen(policy->ip_entity);
++	if (strncasecmp(policy->ip_entity, eid, eidlen)
++	 && (eid[eidlen] == ':' || eid[eidlen] == '\0'))
++		goto reject;
++
++accept:	rv = 1;
++
++reject:
++	isns_debug_auth(":: policy %s entity ID %s %s\n",
++			policy->ip_name, eid,
++			rv? "permitted" : "DENIED");
++	return rv;
++}
++
++const char *
++isns_policy_default_entity(const isns_policy_t *policy)
++{
++	return policy->ip_entity;
++}
++
++int
++isns_policy_validate_node_name(const isns_policy_t *policy,
++			const char *node_name)
++{
++	int	rv = 0;
++
++	/* Control nodes get to do everything */
++	if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
++		goto accept;
++
++	if (!__validate_node_name(policy, node_name))
++		goto reject;
++
++accept:	rv = 1;
++reject:
++	isns_debug_auth(":: policy %s storage node name %s %s\n",
++			policy->ip_name, node_name,
++			rv? "permitted" : "DENIED");
++	return rv;
++}
++
++/*
++ * Check whether the client is allowed to access
++ * the given object in a particular way.
++ */
++static int
++__isns_policy_validate_object_access(const isns_policy_t *policy,
++			const isns_source_t *source,
++			const isns_object_t *obj,
++			isns_object_template_t *tmpl,
++			unsigned int function)
++{
++	uint32_t mask, perm = ISNS_PERMISSION_WRITE;
++	int	rv = 0;
++
++	/* Control nodes get to do everything */
++	if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
++		goto accept;
++
++	if (function == ISNS_DEVICE_ATTRIBUTE_QUERY
++	 || function == ISNS_DEVICE_GET_NEXT)
++		perm = ISNS_PERMISSION_READ;
++
++	/*
++	 * 5.6.1.  Source Attribute
++	 *
++	 * For messages that change the contents of the iSNS
++	 * database, the iSNS server MUST verify that the Source
++	 * Attribute identifies either a Control Node or a Storage
++	 * Node that is a part of the Network Entity containing
++	 * the added, deleted, or modified objects.
++	 *
++	 * Note: this statement makes sense for nodes, portals
++	 * etc, but not for discovery domains, which are not
++	 * part of any network entity (but the Control Node clause
++	 * above still applies).
++	 */
++	if (perm == ISNS_PERMISSION_WRITE && obj != NULL) {
++		const isns_object_t *entity;
++
++		entity = obj->ie_container;
++		if (entity && entity != source->is_entity)
++			goto refuse;
++
++		/* You're not allowed to modify virtual objects */
++		if (obj->ie_rebuild)
++			goto refuse;
++	}
++
++	/* Check whether the client is permitted
++	   to access such an object */
++	mask = ISNS_ACCESS(tmpl->iot_handle, perm);
++	if (!(policy->ip_object_types & mask))
++		goto refuse;
++
++	if (source->is_untrusted && (obj->ie_flags & ISNS_OBJECT_PRIVATE))
++		goto refuse;
++
++accept:
++	rv = 1;
++
++refuse:
++	if (obj) {
++		isns_debug_auth(":: policy %s operation %s on object %08x (%s) %s\n",
++			policy->ip_name,
++			isns_function_name(function),
++			obj->ie_index,
++			tmpl->iot_name,
++			rv? "permitted" : "DENIED");
++	} else {
++		isns_debug_auth(":: policy %s operation %s on %s object %s\n",
++			policy->ip_name,
++			isns_function_name(function),
++			tmpl->iot_name,
++			rv? "permitted" : "DENIED");
++	}
++	return rv;
++}
++
++/*
++ * Check whether the client is allowed to access
++ * the given object. This is called for read functions.
++ */
++int
++isns_policy_validate_object_access(const isns_policy_t *policy,
++			const isns_source_t *source,
++			const isns_object_t *obj,
++			unsigned int function)
++{
++	return __isns_policy_validate_object_access(policy, source,
++			obj, obj->ie_template,
++			function);
++}
++
++/*
++ * Check whether the client is allowed to update
++ * the given object.
++ */
++int
++isns_policy_validate_object_update(const isns_policy_t *policy,
++			const isns_source_t *source,
++			const isns_object_t *obj,
++			const isns_attr_list_t *attrs,
++			unsigned int function)
++{
++	return __isns_policy_validate_object_access(policy, source,
++			obj, obj->ie_template,
++			function);
++}
++
++/*
++ * Check whether the client is allowed to create an object
++ * with the given attrs.
++ */
++int
++isns_policy_validate_object_creation(const isns_policy_t *policy,
++			const isns_source_t *source,
++			isns_object_template_t *tmpl,
++			const isns_attr_list_t *keys,
++			const isns_attr_list_t *attrs,
++			unsigned int function)
++{
++	const char	*name = NULL;
++
++	if (tmpl == &isns_entity_template) {
++		/* DevReg messages may contain an empty EID
++		 * string, which means the server should select
++		 * one. */
++		if (isns_attr_list_get_string(keys,
++				ISNS_TAG_ENTITY_IDENTIFIER, &name)
++		 && !isns_policy_validate_entity(policy, name))
++			return 0;
++	}
++
++	if (tmpl == &isns_iscsi_node_template) {
++		if (isns_attr_list_get_string(keys,
++				ISNS_TAG_ISCSI_NAME, &name)
++		 && !isns_policy_validate_node_name(policy, name))
++			return 0;
++	}
++
++	/* Should we also include the permitted portals
++	 * in the policy? */
++
++	return __isns_policy_validate_object_access(policy, source,
++			NULL, tmpl, function);
++}
++
++/*
++ * Check whether the client is permitted to access
++ * or create an object of this type.
++ * FIXME: Pass R/W permission bit
++ */
++int
++isns_policy_validate_object_type(const isns_policy_t *policy,
++				isns_object_template_t *tmpl,
++				unsigned int function)
++{
++	uint32_t mask;
++	int	rv = 0;
++
++	/* Control nodes get to do everything */
++	if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
++		goto accept;
++
++	mask = ISNS_ACCESS_R(tmpl->iot_handle);
++	if (!(policy->ip_object_types & mask))
++		goto reject;
++
++accept:	rv = 1;
++
++reject:
++	isns_debug_auth(":: policy %s operation %s on object type %s %s\n",
++			policy->ip_name,
++			isns_function_name(function),
++			tmpl->iot_name,
++			rv? "permitted" : "DENIED");
++	return rv;
++}
++
++int
++isns_policy_validate_node_type(const isns_policy_t *policy, uint32_t type)
++{
++	int	rv = 0;
++
++	if ((~policy->ip_node_types & type) == 0)
++		rv = 1;
++
++	isns_debug_auth(":: policy %s registration of node type 0x%x %s\n",
++			policy->ip_name, type,
++			rv? "permitted" : "DENIED");
++	return rv;
++}
++
++/*
++ * 6.4.4.
++ * Management SCNs provide information about all changes to the network,
++ * regardless of discovery domain membership.  Registration for management
++ * SCNs is indicated by setting bit 26 to 1.  Only Control Nodes may
++ * register for management SCNs.  Bits 30 and 31 may only be enabled if
++ * bit 26 is set to 1.
++ */
++int
++isns_policy_validate_scn_bitmap(const isns_policy_t *policy,
++					uint32_t bitmap)
++{
++	int	rv = 1;
++
++	if (policy->ip_node_types & ISNS_ISCSI_CONTROL_MASK)
++		goto accept;
++
++	if (!(bitmap & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK)) {
++		if (bitmap & (ISNS_SCN_DD_MEMBER_ADDED_MASK |
++			      ISNS_SCN_DD_MEMBER_REMOVED_MASK))
++			goto reject;
++		goto accept;
++	}
++
++reject:
++	rv = 0;
++
++accept:
++	isns_debug_auth(":: policy %s scn bitmap 0x%x %s\n",
++			policy->ip_name, bitmap,
++			rv? "permitted" : "DENIED");
++	return rv;
++}
++
++/*
++ * Create the default policy for a given SPI
++ */
++isns_policy_t *
++isns_policy_default(const char *spi, size_t len)
++{
++	return __isns_policy_alloc(spi, len);
++}
++
++/*
++ * Create the policy object for the server we're
++ * talking to. The server is allowed to send us
++ * ESI and SCN messages, and that's about it.
++ */
++isns_policy_t *
++isns_policy_server(void)
++{
++	isns_policy_t	*policy;
++
++	policy = __isns_policy_alloc("<server>", 8);
++
++	policy->ip_functions =
++		(1 << ISNS_ENTITY_STATUS_INQUIRY) |
++		(1 << ISNS_STATE_CHANGE_NOTIFICATION);
++	policy->ip_node_types = 0;
++	policy->ip_object_types = 0;
++	isns_string_array_append(&policy->ip_node_names, "*");
++	return policy;
++}
++
++/*
++ * Allocate an empty policy object
++ */
++isns_policy_t *
++__isns_policy_alloc(const char *spi, size_t len)
++{
++	isns_policy_t	*policy;
++
++	policy = isns_calloc(1, sizeof(*policy));
++	policy->ip_name = isns_malloc(len + 1);
++	policy->ip_users = 1;
++	policy->ip_gen = isns_policy_gen;
++
++	memcpy(policy->ip_name, spi, len);
++	policy->ip_name[len] = '\0';
++
++	/* Only register/query allowed */
++	policy->ip_functions =
++		(1 << ISNS_DEVICE_ATTRIBUTE_REGISTER) |
++		(1 << ISNS_DEVICE_ATTRIBUTE_QUERY) |
++		(1 << ISNS_DEVICE_GET_NEXT) |
++		(1 << ISNS_DEVICE_DEREGISTER) |
++		(1 << ISNS_SCN_REGISTER);
++
++	/* Can only register initiator node(s) */
++	policy->ip_node_types =
++		ISNS_ISCSI_INITIATOR_MASK;
++
++	/* Can only view/modify standard objects */
++	policy->ip_object_types = ISNS_DEFAULT_OBJECT_ACCESS;
++
++	return policy;
++}
++
++/*
++ * Release a policy object
++ */
++void
++isns_policy_release(isns_policy_t *policy)
++{
++	if (!policy)
++		return;
++
++	isns_assert(policy->ip_users);
++	if (--(policy->ip_users))
++		return;
++
++	isns_assert(policy != &isns_superhero_powers);
++	isns_assert(policy != &isns_flyingpigs_powers);
++	isns_assert(policy != &isns_dweeb_powers);
++
++	isns_free(policy->ip_name);
++	isns_free(policy->ip_entity);
++	isns_free(policy->ip_dd_default);
++	isns_string_array_destroy(&policy->ip_node_names);
++
++	isns_free(policy);
++}
+diff --git a/utils/open-isns/portal-group.c b/utils/open-isns/portal-group.c
+new file mode 100644
+index 0000000..647bbde
+--- /dev/null
++++ b/utils/open-isns/portal-group.c
+@@ -0,0 +1,307 @@
++/*
++ * iSNS object model - portal group specific code
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include "isns.h"
++#include "objects.h"
++#include "vendor.h"
++#include "attrs.h"
++#include "util.h"
++
++/* For relationship stuff - should go */
++#include "db.h"
++
++
++/*
++ * Retrieve attribute @old_tag from object @obj, create a copy with
++ * tag @new_tag, and append it to list @dst.
++ * (Helper function for the portal group stuff)
++ */
++static int
++__isns_object_translate_attr(isns_object_t *obj,
++		uint32_t old_tag, uint32_t new_tag,
++		isns_attr_list_t *dst)
++{
++	isns_value_t	value;
++
++	if (!isns_attr_list_get_value(&obj->ie_attrs, old_tag, &value))
++		return 0;
++	isns_attr_list_append_value(dst, new_tag, NULL, &value);
++	return 1;
++}
++
++
++/*
++ * Portal Group
++ */
++static isns_object_t *
++__isns_pg_create(const isns_attr_list_t *attrs, uint32_t pg_tag,
++		isns_object_t *portal, isns_object_t *node)
++{
++	isns_object_t	*obj;
++
++	obj = isns_create_object(&isns_iscsi_pg_template, attrs,
++			isns_object_get_entity(portal));
++
++	/*
++	 * 3.4
++	 *
++	 * Each Portal and iSCSI Storage Node registered in an Entity can
++	 * be associated using a Portal Group (PG) object.  The PG Tag
++	 * (PGT), if non-NULL, indicates that the associated Portal
++	 * provides access to the associated iSCSI Storage Node in
++	 * the Entity.	All Portals that have the same PGT value for
++	 * a specific iSCSI Storage Node allow coordinated access to
++	 * that node.
++	 *
++	 * 5.6.5.2
++	 *
++	 * If the PGT of the Portal Group is not NULL, then a relationship
++	 * exists between the indicated Storage Node and Portal; if the
++	 * PGT is NULL, then no relationship exists.
++	 */
++	if (pg_tag != 0) {
++		isns_object_set_uint32(obj,
++				ISNS_TAG_PG_TAG, pg_tag);
++	} else {
++		/* A NULL PGT indicates that the
++		 * storage node cannot be accessed through
++		 * this portal. */
++		isns_object_set_nil(obj, ISNS_TAG_PG_TAG);
++	}
++
++	/* This object represents a relationship between portal
++	   and storage node. Create a relation. */
++	obj->ie_relation = isns_create_relation(obj,
++			ISNS_RELATION_PORTAL_GROUP,
++			portal, node);
++
++	return obj;
++}
++
++/*
++ * Find the portal for a given portal group
++ */
++static isns_object_t *
++__isns_pg_find_portal(isns_db_t *db, isns_object_t *pg,
++		const isns_object_list_t *extra_objs)
++{
++	isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
++	isns_object_t	*obj = NULL;
++
++	/* FIXME: ISNS_TAG_PG_PORTAL_IP_ADDR -> ...ADDRESS */
++	if (!__isns_object_translate_attr(pg,
++				ISNS_TAG_PG_PORTAL_IP_ADDR,
++				ISNS_TAG_PORTAL_IP_ADDRESS,
++				&key_attrs))
++		goto out;
++	if (!__isns_object_translate_attr(pg,
++				ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
++				ISNS_TAG_PORTAL_TCP_UDP_PORT,
++				&key_attrs))
++		goto out;
++
++	obj = isns_db_lookup(db, &isns_portal_template, &key_attrs);
++	if (!obj && extra_objs)
++		obj = isns_object_list_lookup(extra_objs,
++				&isns_portal_template, &key_attrs);
++
++out:
++	isns_attr_list_destroy(&key_attrs);
++	return obj;
++}
++
++/*
++ * Find the node for a given portal group
++ */
++static isns_object_t *
++__isns_pg_find_node(isns_db_t *db, isns_object_t *pg,
++		const isns_object_list_t *extra_objs)
++{
++	isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
++	isns_object_t	*obj = NULL;
++
++	if (!__isns_object_translate_attr(pg,
++				ISNS_TAG_PG_ISCSI_NAME,
++				ISNS_TAG_ISCSI_NAME,
++				&key_attrs))
++		goto out;
++
++	obj = isns_db_lookup(db, &isns_iscsi_node_template, &key_attrs);
++	if (!obj && extra_objs)
++		obj = isns_object_list_lookup(extra_objs,
++				&isns_iscsi_node_template, &key_attrs);
++
++out:
++	isns_attr_list_destroy(&key_attrs);
++	return obj;
++}
++
++/*
++ * When creating a portal group, it must not connect nodes and
++ * portals from other entities. However, it is perfectly fine to
++ * link objects in limbo.
++ */
++static inline int
++__isns_pg_may_relate(isns_object_t *entity, isns_object_t *subordinate)
++{
++	isns_object_t *other;
++
++	other = isns_object_get_entity(subordinate);
++	return other == NULL || other == entity;
++}
++
++/*
++ * Given a portal group object, create the relationship
++ */
++isns_relation_t *
++isns_db_build_pg_relation(isns_db_t *db, isns_object_t *pg,
++		const isns_object_list_t *extra_objs)
++{
++	isns_object_t   *entity, *node = NULL, *portal = NULL;
++
++	entity = isns_object_get_entity(pg);
++
++	node = __isns_pg_find_node(db, pg, extra_objs);
++	if (node == NULL) {
++		isns_error("Trying to register PG for non-existant node\n");
++		goto failed;
++	}
++	if (!__isns_pg_may_relate(entity, node)) {
++		isns_error("Trying to register PG for node in other entity\n");
++		goto failed;
++	}
++
++	portal = __isns_pg_find_portal(db, pg, extra_objs);
++	if (portal == NULL) {
++		isns_error("Trying to register PG for non-existant portal\n");
++		goto failed;
++	}
++	if (!__isns_pg_may_relate(entity, portal)) {
++		isns_error("Trying to register PG for portal in other entity\n");
++		goto failed;
++	}
++
++	pg->ie_relation = isns_create_relation(pg,
++				ISNS_RELATION_PORTAL_GROUP,
++				node, portal);
++	isns_object_release(portal);
++	isns_object_release(node);
++
++	return pg->ie_relation;
++
++failed:
++	if (portal)
++		isns_object_release(portal);
++	if (node)
++		isns_object_release(node);
++	return NULL;
++}
++
++/*
++ * Create a portal group given node, portal and PGT
++ */
++isns_object_t *
++isns_create_portal_group(isns_object_t *portal,
++		isns_object_t *node, uint32_t pg_tag)
++{
++	isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
++	isns_object_t	*obj = NULL;
++
++	if (portal == NULL || node == NULL)
++		return NULL;
++
++	if (node->ie_container != portal->ie_container) {
++		isns_error("Refusing to create portal group "
++			   "linking objects from different entities\n");
++		return NULL;
++	}
++
++	if (__isns_object_translate_attr(node,
++				ISNS_TAG_ISCSI_NAME,
++				ISNS_TAG_PG_ISCSI_NAME,
++				&key_attrs)
++	 && __isns_object_translate_attr(portal,
++		 		ISNS_TAG_PORTAL_IP_ADDRESS,
++				ISNS_TAG_PG_PORTAL_IP_ADDR,
++				&key_attrs)
++	 && __isns_object_translate_attr(portal,
++		 		ISNS_TAG_PORTAL_TCP_UDP_PORT,
++				ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
++				&key_attrs)) {
++		obj = __isns_pg_create(&key_attrs, pg_tag, portal, node);
++	}
++
++	isns_attr_list_destroy(&key_attrs);
++	return obj;
++}
++
++/*
++ * 5.6.5.1
++ * New PG objects are registered when an associated Portal or
++ * iSCSI Node object is registered.  An explicit PG object
++ * registration MAY follow a Portal or iSCSI Node object
++ * registration in a DevAttrReg message.
++ * [...]
++ * If the PGT value is not included in the Storage Node or
++ * Portal object registration, and if a PGT value was not
++ * previously registered for the relationship, then the PGT for
++ * the corresponding PG object SHALL be registered with a value
++ * of 0x00000001.
++ *
++ * We return non-NULL if the object was created.
++ */
++isns_object_t *
++isns_create_default_portal_group(isns_db_t *db,
++		isns_object_t *portal, isns_object_t *node)
++{
++	isns_object_t	*obj;
++
++	if (portal == NULL || node == NULL)
++		return 0;
++
++	/* See if there is a PG already */
++	obj = isns_db_get_relationship_object(db, node, portal,
++			ISNS_RELATION_PORTAL_GROUP);
++	if (obj != NULL) {
++		isns_object_release(obj);
++		return NULL;
++	}
++
++	return isns_create_portal_group(portal, node, 1);
++}
++
++static uint32_t	iscsi_pg_attrs[] = {
++	ISNS_TAG_PG_ISCSI_NAME,
++	ISNS_TAG_PG_PORTAL_IP_ADDR,
++	ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
++	ISNS_TAG_PG_TAG,
++	ISNS_TAG_PG_INDEX,
++};
++
++static uint32_t	iscsi_pg_key_attrs[] = {
++	ISNS_TAG_PG_ISCSI_NAME,
++	ISNS_TAG_PG_PORTAL_IP_ADDR,
++	ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
++};
++
++isns_object_template_t		isns_iscsi_pg_template = {
++	.iot_name	= "iSCSI Portal Group",
++	.iot_handle	= ISNS_OBJECT_TYPE_PG,
++	.iot_attrs	= iscsi_pg_attrs,
++	.iot_num_attrs	= array_num_elements(iscsi_pg_attrs),
++	.iot_keys	= iscsi_pg_key_attrs,
++	.iot_num_keys	= array_num_elements(iscsi_pg_key_attrs),
++	.iot_attrs	= iscsi_pg_attrs,
++	.iot_keys	= iscsi_pg_key_attrs,
++	.iot_index	= ISNS_TAG_PG_INDEX,
++	.iot_next_index	= ISNS_TAG_PG_NEXT_INDEX,
++	.iot_container	= &isns_entity_template,
++	.iot_relation_type = ISNS_RELATION_PORTAL_GROUP,
++	.iot_build_relation = isns_db_build_pg_relation,
++};
++
+diff --git a/utils/open-isns/query.c b/utils/open-isns/query.c
+new file mode 100644
+index 0000000..b2cfbc9
+--- /dev/null
++++ b/utils/open-isns/query.c
+@@ -0,0 +1,238 @@
++/*
++ * Handle iSNS Device Attribute Query
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include "isns.h"
++#include "attrs.h"
++#include "message.h"
++#include "security.h"
++#include "objects.h"
++#include "db.h"
++#include "util.h"
++
++/*
++ * Create a query, and set the source name
++ */
++static isns_simple_t *
++__isns_create_query(isns_source_t *source, const isns_attr_list_t *key)
++{
++	return isns_simple_create(ISNS_DEVICE_ATTRIBUTE_QUERY, source, key);
++}
++
++isns_simple_t *
++isns_create_query(isns_client_t *clnt, const isns_attr_list_t *key)
++{
++	return __isns_create_query(clnt->ic_source, key);
++}
++
++isns_simple_t *
++isns_create_query2(isns_client_t *clnt, const isns_attr_list_t *key, isns_source_t *source)
++{
++	return __isns_create_query(source?: clnt->ic_source, key);
++}
++
++int
++isns_query_request_attr_tag(isns_simple_t *qry, uint32_t tag)
++{
++	isns_attr_list_append_nil(&qry->is_operating_attrs, tag);
++	return ISNS_SUCCESS;
++}
++
++int
++isns_query_request_attr(isns_simple_t *qry, isns_attr_t *attr)
++{
++	if (!ISNS_ATTR_IS_NIL(attr)) {
++		isns_error("Query operating attribute must be NIL\n");
++		return ISNS_INVALID_QUERY;
++	}
++	isns_attr_list_append_attr(&qry->is_operating_attrs, attr);
++	return ISNS_SUCCESS;
++}
++
++static unsigned int
++isns_query_get_requested_types(const isns_attr_list_t *attrs)
++{
++	unsigned int	i, mask = 0;
++
++	for (i = 0; i < attrs->ial_count; ++i) {
++		uint32_t tag = attrs->ial_data[i]->ia_tag_id;
++		isns_object_template_t *tmpl;
++
++		tmpl = isns_object_template_find(tag);
++		/* Ignore unknown tags */
++		if (tmpl == NULL)
++			continue;
++
++		mask |= 1 << tmpl->iot_handle;
++	}
++	return mask;
++}
++
++/*
++ * Get the list of objects matching this query
++ */
++static int
++isns_query_get_objects(isns_simple_t *qry, isns_db_t *db, isns_object_list_t *result)
++{
++	isns_scope_t		*scope = NULL;
++	isns_object_list_t	matching = ISNS_OBJECT_LIST_INIT;
++	isns_attr_list_t	*keys = &qry->is_message_attrs;
++	isns_object_template_t	*query_type = NULL;
++	unsigned int		i, qry_mask = 0;
++	int			status;
++
++	/* 5.6.5.2
++	 * If multiple attributes are used as the Message Key, then they
++	 * MUST all be from the same object type (e.g., IP address and
++	 * TCP/UDP Port are attributes of the Portal object type).
++	 */
++	for (i = 0; i < keys->ial_count; ++i) {
++		isns_object_template_t	*tmpl;
++		uint32_t tag = keys->ial_data[i]->ia_tag_id;
++
++		tmpl = isns_object_template_for_tag(tag);
++		if (tmpl == NULL)
++			return ISNS_ATTRIBUTE_NOT_IMPLEMENTED;
++		if (query_type == NULL)
++			query_type = tmpl;
++		else if (tmpl != query_type)
++			return ISNS_INVALID_QUERY;
++	}
++
++	/*
++	 * 5.6.5.2
++	 * An empty Message Key field indicates the query is scoped to
++	 * the entire database accessible by the source Node.
++	 */
++	if (keys->ial_count == 0) {
++		query_type = &isns_entity_template;
++		keys = NULL;
++	}
++
++	/* Policy: check whether the client is allowed to
++	 * query this type of object. */
++	if (!isns_policy_validate_object_type(qry->is_policy,
++				query_type, qry->is_function))
++		return ISNS_SOURCE_UNAUTHORIZED;
++
++	/* No scope means that the source is not part of
++	 * any discovery domain, and there's no default DD.
++	 * Just return an empty reply. */
++	scope = isns_scope_for_call(db, qry);
++	if (scope == NULL)
++		return ISNS_SUCCESS;
++
++	status = isns_scope_gang_lookup(scope, query_type, keys, &matching);
++	if (status != ISNS_SUCCESS)
++		goto out;
++
++	/* Extract the mask of requested objects */
++	qry_mask = isns_query_get_requested_types(&qry->is_operating_attrs);
++
++	/*
++	 * 5.6.5.2
++	 * The DevAttrQry response message returns attributes of objects
++	 * listed in the Operating Attributes that are related to the
++	 * Message Key of the original DevAttrQry message.
++	 */
++	for (i = 0; i < matching.iol_count; ++i) {
++		isns_object_t	*obj = matching.iol_data[i];
++
++		if (!isns_policy_validate_object_access(qry->is_policy,
++					qry->is_source, obj,
++					qry->is_function))
++			continue;
++
++		if (obj->ie_container)
++			isns_object_list_append(result, obj->ie_container);
++		isns_object_list_append(result, obj);
++		isns_scope_get_related(scope, obj, qry_mask, result);
++	}
++
++out:
++	isns_object_list_destroy(&matching);
++	isns_scope_release(scope);
++	return status;
++}
++
++/*
++ * Create a Query Response
++ */
++static isns_simple_t *
++isns_create_query_response(isns_server_t *srv,
++		const isns_simple_t *qry, const isns_object_list_t *objects)
++{
++	const isns_attr_list_t *req_attrs = NULL;
++	isns_simple_t	*resp;
++	unsigned int	i;
++
++	resp = __isns_create_query(srv->is_source, &qry->is_message_attrs);
++
++	/*
++	 * 5.7.5.2.
++	 * If no Operating Attributes are included in the original
++	 * query, then all Operating Attributes SHALL be returned
++	 * in the response.
++	 */
++	if (qry->is_operating_attrs.ial_count != 0)
++		req_attrs = &qry->is_operating_attrs;
++
++	for (i = 0; i < objects->iol_count; ++i) {
++		isns_object_t	*obj = objects->iol_data[i];
++
++		if (obj->ie_rebuild)
++			obj->ie_rebuild(obj, srv->is_db);
++		isns_object_get_attrlist(obj,
++				&resp->is_operating_attrs,
++				req_attrs);
++	}
++	return resp;
++}
++
++int
++isns_process_query(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
++{
++	isns_object_list_t	objects = ISNS_OBJECT_LIST_INIT;
++	isns_simple_t		*reply = NULL;
++	isns_db_t		*db = srv->is_db;
++	int			status;
++
++	/* Get the objects matching the query */
++	status = isns_query_get_objects(call, db, &objects);
++	if (status != ISNS_SUCCESS)
++		goto done;
++
++	/* Success: build the response */
++	reply = isns_create_query_response(srv, call, &objects);
++	if (reply == NULL) {
++		status = ISNS_INTERNAL_ERROR;
++		goto done;
++	}
++
++	/* There's nothing in the spec that tells us what to
++	 * return if the query matches no object.
++	 */
++	if (objects.iol_count == 0) {
++		status = ISNS_NO_SUCH_ENTRY;
++		goto done;
++	}
++
++done:
++	isns_object_list_destroy(&objects);
++	*result = reply;
++	return status;
++}
++
++/*
++ * Parse the list of objects in a query response
++ */
++int
++isns_query_response_get_objects(isns_simple_t *qry,
++		isns_object_list_t *result)
++{
++	return isns_simple_response_get_objects(qry, result);
++}
+diff --git a/utils/open-isns/register.c b/utils/open-isns/register.c
+new file mode 100644
+index 0000000..120deae
+--- /dev/null
++++ b/utils/open-isns/register.c
+@@ -0,0 +1,934 @@
++/*
++ * Handle iSNS Device Attribute Registration
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include "isns.h"
++#include "attrs.h"
++#include "objects.h"
++#include "message.h"
++#include "security.h"
++#include "util.h"
++#include "db.h"
++
++
++static int	isns_create_default_pgs_for_object(isns_db_t *, isns_object_t *);
++
++/*
++ * Create a registration, and set the source name
++ */
++static isns_simple_t *
++__isns_create_registration(isns_source_t *source, isns_object_t *key_obj)
++{
++	isns_simple_t	*reg;
++
++	reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER, source, NULL);
++	if (reg == NULL)
++		return NULL;
++
++	/*
++	 * When sending a registration, you can either specify
++	 * the object to be modified in the key attrs, or leave
++	 * the key empty.
++	 */
++	if (key_obj == NULL)
++		return reg;
++
++	/* User gave us a key object. We need to put the key
++	 * attributes into the message attrs, and *all* attrs
++	 * into the operating attrs. */
++	if (!isns_object_extract_keys(key_obj, &reg->is_message_attrs)) {
++		/* bummer - seems the object is missing some
++		 * vital organs. */
++		isns_warning("%s: object not fully specified, key attrs missing\n",
++				__FUNCTION__);
++		goto failed;
++	}
++
++	/*
++	 * The Message Key identifies the object the DevAttrReg message
++	 * acts upon.  [...] The key attribute(s) identifying this object
++	 * MUST also be included among the Operating Attributes.
++	 *
++	 * We do not enforce this here, we rely on the caller to get this
++	 * right.
++	 */
++#if 0
++	if (!isns_object_extract_all(key_obj, &reg->is_operating_attrs)) {
++		isns_warning("%s: unable to extract attrs from key objects\n",
++				__FUNCTION__);
++		goto failed;
++	}
++#endif
++
++	return reg;
++
++failed:
++	isns_simple_free(reg);
++	return NULL;
++}
++
++isns_simple_t *
++isns_create_registration(isns_client_t *clnt, isns_object_t *key_obj)
++{
++	return __isns_create_registration(clnt->ic_source, key_obj);
++}
++
++isns_simple_t *
++isns_create_registration2(isns_client_t *clnt, isns_object_t *key_obj,
++		isns_source_t *source)
++{
++	return __isns_create_registration(source?: clnt->ic_source, key_obj);
++}
++
++/*
++ * Set the replace flag
++ */
++void
++isns_registration_set_replace(isns_simple_t *reg, int replace)
++{
++	reg->is_replace = !!replace;
++}
++
++/*
++ * Add an object to the registration
++ */
++void
++isns_registration_add_object(isns_simple_t *reg, isns_object_t *obj)
++{
++	isns_object_extract_writable(obj, &reg->is_operating_attrs);
++}
++
++void
++isns_registration_add_object_list(isns_simple_t *reg, isns_object_list_t *list)
++{
++	unsigned int i;
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_extract_writable(list->iol_data[i],
++				&reg->is_operating_attrs);
++	}
++}
++
++/*
++ * Get the key object given in this message
++ *
++ * It doesn't say anywhere explicitly in the RFC, but
++ * the message key can contain both key and non-key
++ * attributes. For instance, you can search by
++ * Portal Group Index (section 3.4).
++ */
++static int
++isns_registration_get_key(isns_simple_t *reg, isns_db_t *db, isns_object_t **key_obj)
++{
++	isns_attr_list_t *keys = &reg->is_message_attrs;
++	isns_attr_list_t dummy_keys = ISNS_ATTR_LIST_INIT;
++	isns_attr_t	*attr;
++	isns_object_t	*obj = NULL;
++	const char	*eid = NULL;
++	char		eidbuf[128];
++	int		status = ISNS_SUCCESS;
++	int		obj_must_exist = 0;
++
++	/*
++	 * 5.6.5.1
++	 * If the Message Key is not present, then the DevAttrReg message
++	 * implicitly registers a new Network Entity.  In this case,
++	 * the replace bit SHALL be ignored; a new Network Entity SHALL
++	 * be created.
++	 *
++	 * Note that some clients seem to leave the message key
++	 * empty, but hide the entity identifier in the operating
++	 * attrs.
++	 */
++	if (keys->ial_count != 0) {
++		attr = keys->ial_data[0];
++
++		/*
++		 * 5.6.5.1
++		 * If the Message Key does not contain an EID, and no
++		 * pre-existing objects match the Message Key, then the
++		 * DevAttrReg message SHALL be rejected with a status
++		 * code of 3 (Invalid Registration).
++		 */
++		if (keys->ial_count != 1
++		 || attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER)
++			obj_must_exist = 1;
++	} else {
++		/* Empty message key. But the client may have hidden
++		 * the EID in the operating attrs :-/
++		 */
++		if (reg->is_operating_attrs.ial_count == 0)
++			goto create_entity;
++
++		attr = reg->is_operating_attrs.ial_data[0];
++		if (attr->ia_tag_id != ISNS_TAG_ENTITY_IDENTIFIER)
++			goto create_entity;
++
++		isns_attr_list_append_attr(&dummy_keys, attr);
++		keys = &dummy_keys;
++	}
++
++	/* If the caller specifies an EID, extract it while
++	 * we know what we're doing :-) */
++	if (attr->ia_tag_id == ISNS_TAG_ENTITY_IDENTIFIER
++	 && ISNS_ATTR_IS_STRING(attr))
++		eid = attr->ia_value.iv_string;
++
++	/* Look up the object identified by the keys.
++	 * We do not scope the lookup, as the client
++	 * may want to add nodes to an entity that's
++	 * currently empty - and hence not visible to
++	 * any DD. */
++	if (!ISNS_ATTR_IS_NIL(attr))
++		obj = isns_db_lookup(db, NULL, keys);
++
++	if (obj == NULL && obj_must_exist)
++		goto err_invalid;
++
++	if (obj != NULL) {
++		/*
++		 * Policy: verify that the client is permitted
++		 * to access this object.
++		 *
++		 * This includes
++		 *  -	the client node must be the object owner,
++		 *	or a control node.
++		 *  -	the policy must allow modification of
++		 *	this object type.
++		 */
++		if (!isns_policy_validate_object_access(reg->is_policy,
++					reg->is_source,
++					obj, reg->is_function))
++			goto err_unauthorized;
++
++found_object:
++		if (reg->is_replace) {
++			isns_object_t *container = NULL;
++
++			if (!ISNS_IS_ENTITY(obj)) {
++				container = isns_object_get_entity(obj);
++				if (container == NULL) {
++					isns_error("Trying to replace %s (id %u) "
++						   "which has no container\n",
++						obj->ie_template->iot_name,
++						obj->ie_index);
++					goto err_invalid;
++				}
++			}
++
++			isns_debug_state("Replacing %s (id %u)\n",
++				obj->ie_template->iot_name,
++				obj->ie_index);
++			isns_db_remove(db, obj);
++			isns_object_release(obj);
++
++			/* Purge the deleted objects from the database now */
++			isns_db_purge(db);
++
++			/* We need to flush pending SCNs because the
++			 * objects may be resurrected from limbo,
++			 * and we might be looking at stale data. */
++			isns_scn_transmit_all();
++
++			/* It's an entity. Nuke it and create
++			 * a new one. */
++			if (container == NULL) {
++				isns_source_set_entity(reg->is_source, NULL);
++				goto create_entity;
++			}
++
++			obj = isns_object_get(container);
++		}
++
++		goto out;
++	}
++
++	/*
++	 * If the Message Key contains an EID and no pre-existing objects
++	 * match the Message Key, then the DevAttrReg message SHALL create a
++	 * new Entity with the specified EID and any new object(s) specified
++	 * by the Operating Attributes.  The replace bit SHALL be ignored.
++	 *
++	 * Implementer's note: the EID attribute may be empty, in which case
++	 * we also create a new entity.
++	 */
++
++create_entity:
++	if (!isns_policy_validate_object_creation(reg->is_policy,
++				reg->is_source,
++				&isns_entity_template, keys, NULL,
++				reg->is_function))
++		goto err_unauthorized;
++
++	/*
++	 * 5.6.5.1
++	 * A registration message that creates a new Network Entity object
++	 * MUST contain at least one Portal or one Storage Node.  If the
++	 * message does not, then it SHALL be considered invalid and result
++	 * in a response with Status Code of 3 (Invalid Registration).
++	 */
++	/* FIXME: Implement this check */
++
++	/* We try to play nice with lazy clients and attempt to
++	 * look up the network entity given the source name.
++	 * But we don't do this if a non-NULL EID was given,
++	 * because the client may explicitly want to specify more
++	 * than one Network Entity.
++	 */
++	if (eid == NULL) {
++		obj = reg->is_source->is_entity;
++		if (obj != NULL) {
++			isns_object_get(obj);
++			goto found_object;
++		}
++
++		/* The policy may define a default entity name.
++		 * If that is the case, use it.
++		 */
++		eid = isns_policy_default_entity(reg->is_policy);
++		if (eid) {
++			obj = isns_db_vlookup(db, &isns_entity_template,
++					ISNS_TAG_ENTITY_IDENTIFIER, eid,
++					0);
++			if (obj) {
++				reg->is_source->is_entity = isns_object_get(obj);
++				goto found_object;
++			}
++		}
++	}
++
++	/*
++	 * 5.6.5.1
++	 * If the Message Key and Operating Attributes do not contain
++	 * an EID attribute, or if the EID attribute has a length of 0,
++	 * then a new Network Entity object SHALL be created and the iSNS
++	 * server SHALL supply a unique EID value for it.
++	 */
++	if (eid == NULL)
++		eid = isns_db_generate_eid(db, eidbuf, sizeof(eidbuf));
++
++	/*
++	 * 6.2.2.  Entity Protocol
++	 *
++	 * This attribute is required during initial registration of
++	 * the Network Entity.
++	 *
++	 * Implementer's note: we don't rely on this. Instead, the
++	 * Entity Protocol is selected based on the source type.
++	 * If the client specifies the protocol, the auto-selected
++	 * value is overwritten.
++	 */
++	obj = isns_create_entity_for_source(reg->is_source, eid);
++	if (obj == NULL)
++		goto err_invalid;
++
++	isns_source_set_entity(reg->is_source, obj);
++
++	/*
++	 * 6.2.6
++	 * If a Registration Period is not requested by the iSNS
++	 * client and Entity Status Inquiry (ESI) messages are not
++	 * enabled for that client, then the Registration Period
++	 * SHALL be set to a non-zero value by the iSNS server.
++	 * This implementation-specific value for the Registration
++	 * Period SHALL be returned in the registration response to the
++	 * iSNS client.  The Registration Period may be set to zero,
++	 * indicating its non-use, only if ESI messages are enabled for
++	 * that Network Entity.
++	 *
++	 * Implementer's note: we diverge from this in two ways:
++	 *  -	the admin may choose to disable registration timeout,
++	 *	by setting RegistrationPeriod=0 in the config file
++	 *
++	 *  -	When a new entity is created, we always set the
++	 *	registration interval because we cannot know yet
++	 *	whether the client will subsequently enable ESI or
++	 *	not.
++	 *
++	 *  -	The control entity (holding policy objects) will
++	 *	not expire.
++	 */
++	if (isns_config.ic_registration_period
++	 && strcasecmp(eid, ISNS_ENTITY_CONTROL)) {
++		isns_object_set_uint32(obj,
++				ISNS_TAG_REGISTRATION_PERIOD,
++				isns_config.ic_registration_period);
++		isns_object_set_uint64(obj,
++				ISNS_TAG_TIMESTAMP,
++				time(NULL));
++	}
++
++	/* Insert into database, and set the object's owner */
++	isns_db_insert(db, obj);
++
++	reg->is_replace = 0;
++
++out:
++	*key_obj = obj;
++	isns_attr_list_destroy(&dummy_keys);
++	return ISNS_SUCCESS;
++
++error:
++	if (obj)
++		isns_object_release(obj);
++	isns_attr_list_destroy(&dummy_keys);
++	return status;
++
++err_unauthorized:
++	status = ISNS_SOURCE_UNAUTHORIZED;
++	goto error;
++
++err_invalid:
++	status = ISNS_INVALID_REGISTRATION;
++	goto error;
++}
++
++static int
++isns_registration_get_next_object(isns_db_t *db,
++				struct isns_attr_list_scanner *st,
++				isns_object_list_t *result)
++{
++	isns_object_t	*current;
++	int		status, esi = 0;
++
++	status = isns_attr_list_scanner_next(st);
++	/* We get here if the registration has a trailing PGT */
++	if (status == ISNS_NO_SUCH_ENTRY)
++		return ISNS_SUCCESS;
++	if (status)
++		return status;
++
++	/*
++	 * Validate the attrlist.
++	 * This makes sure the client does not include
++	 * duplicate attributes, readonly attributes
++	 * such as Registration Timestamp, Index and Next Index,
++	 * or privileged data (such as marking a storage node as
++	 * control node).
++	 */
++	status = isns_attr_list_validate(&st->attrs,
++			st->policy,
++			ISNS_DEVICE_ATTRIBUTE_REGISTER);
++	if (status) {
++		isns_debug_protocol("invalid attr in message\n");
++		return status;
++	}
++
++	/*
++	 * 6.3.4.  Entity Status Inquiry Interval
++	 *
++	 * If the iSNS server is unable to support ESI messages
++	 * or the ESI Interval requested, it SHALL [...] reject
++	 * the ESI request by returning an "ESI Not Available"
++	 * Status Code [...]
++	 *
++	 * Implementer's note: In section 5.7.5.1, the RFC talks
++	 * about modifying the requested ESI interval; so it seems
++	 * it's okay to be liberal about the ESI intervals we accept,
++	 * and update them quietly.
++	 */
++	if (isns_attr_list_contains(&st->attrs, ISNS_TAG_ESI_PORT)) {
++		if (!isns_esi_enabled) {
++			isns_debug_esi("Refusing to accept portal "
++					"registration with ESI port\n");
++			return ISNS_ESI_NOT_AVAILABLE;
++		}
++		esi = 1;
++	}
++
++	/*
++	 * Override any registration period specified by the client.
++	 */
++	if (isns_attr_list_contains(&st->attrs, ISNS_TAG_REGISTRATION_PERIOD)) {
++		isns_value_t value = ISNS_VALUE_INIT(uint32,
++					isns_config.ic_registration_period);
++
++		isns_attr_list_update_value(&st->attrs,
++				ISNS_TAG_REGISTRATION_PERIOD, NULL,
++				&value);
++	}
++
++	if (st->tmpl == &isns_entity_template) {
++		/*
++		 * 5.6.5.1.
++		 * A maximum of one Network Entity object can be
++		 * created or updated with a single DevAttrReg
++		 * message.  Consequently, the Operating Attributes
++		 * MUST NOT contain more than one Network Entity
++		 * object.
++		 */
++		if (st->entities++) {
++			isns_debug_protocol("More than one entity in DevAttrReg msg\n");
++			return ISNS_INVALID_REGISTRATION;
++		}
++
++		/* This should be the key object.
++		 * The EID specified by by the client may be
++		 * empty, so don't overwrite the value we
++		 * assigned with something else.
++		 */
++		if (!isns_object_match(st->key_obj, &st->keys)) {
++			isns_debug_protocol("Entity mismatch in message vs. operating attrs\n");
++			return ISNS_INVALID_REGISTRATION;
++		}
++		current = isns_object_get(st->key_obj);
++	} else
++	if (st->tmpl == &isns_dd_template || st->tmpl == &isns_ddset_template) {
++		isns_debug_protocol("DevAttrReg of type %s not allowed\n",
++				st->tmpl->iot_name);
++		return ISNS_INVALID_REGISTRATION;
++	} else {
++		/* This will also catch objects in limbo. */
++		current = isns_db_lookup(db, st->tmpl, &st->keys);
++	}
++
++	if (current != NULL) {
++		/* 
++		 * If the replace bit is not set, then the message updates
++		 * the attributes of the object identified by the Message Key
++		 * and its subordinate objects.  Existing object containment
++		 * relationships MUST NOT be changed.  For existing objects,
++		 * key attributes MUST NOT be modified, but new subordinate
++		 * objects MAY be added.
++		 */
++
++		/*
++		 * [...]
++		 * If the Node identified by the Source Attribute is
++		 * not a Control Node, then the objects in the operating
++		 * attributes MUST be members of the same Network Entity
++		 * as the Source Node.
++		 */
++		if (!isns_policy_validate_object_update(st->policy,
++					st->source, current, &st->attrs,
++					ISNS_DEVICE_ATTRIBUTE_REGISTER)) {
++			isns_object_release(current);
++			return ISNS_SOURCE_UNAUTHORIZED;
++		}
++
++		/* We shouldn't allow messages affecting one Entity
++		 * to modify objects owned by a different Entity.
++		 *
++		 * However, there may be orphan objects (created
++		 * while populating discovery domains). These will
++		 * not be associated with any Network Entity, so
++		 * they're up for grabs.
++		 */
++		if (st->key_obj == current
++		 || st->key_obj == current->ie_container) {
++			/* All is well. The current object is the
++			 * key object itself, or a direct descendant of the
++			 * key object. */
++			/* FIXME: with FC we can get deeper nesting;
++			 * this needs work. */
++		} else
++		if (!isns_object_is_valid_container(st->key_obj, st->tmpl)) {
++			isns_error("Client attempts to add %s object to a %s - tsk tsk.\n",
++					st->tmpl->iot_name,
++					st->key_obj->ie_template->iot_name);
++			goto invalid_registration;
++		} else if (current->ie_container) {
++			/* We shouldn't get here in authenticated mode,
++			 * but in insecure mode we still may. */
++			isns_error("Client attempts to move %s %u to a different %s\n",
++					current->ie_template->iot_name,
++					current->ie_index,
++					st->key_obj->ie_template->iot_name);
++			goto invalid_registration;
++		}
++	} else {
++		if (!isns_object_is_valid_container(st->key_obj, st->tmpl)) {
++			isns_error("Client attempts to add %s object to a %s - tsk tsk.\n",
++					st->tmpl->iot_name,
++					st->key_obj->ie_template->iot_name);
++			goto invalid_registration;
++		}
++
++		if (!isns_policy_validate_object_creation(st->policy,
++					st->source, st->tmpl,
++					&st->keys, &st->attrs,
++					ISNS_DEVICE_ATTRIBUTE_REGISTER)) {
++			return ISNS_SOURCE_UNAUTHORIZED;
++		}
++		current = isns_create_object(st->tmpl, &st->keys,
++				isns_object_get_entity(st->key_obj));
++
++		/* We do not insert the new object into the database yet.
++		 * That happens after we're done with parsing *all*
++		 * objects. */
++	}
++
++	if (!isns_object_set_attrlist(current, &st->attrs)) {
++		isns_debug_state("Error updating object's attrlist\n");
++		isns_object_release(current);
++		return ISNS_INTERNAL_ERROR;
++	}
++
++	/* If the client specifies an ESI port, make sure the
++	 * ESI interval is set and within bounds. */
++	if (esi) {
++		uint32_t	esi_interval;
++
++		if (!isns_object_get_uint32(current,
++					ISNS_TAG_ESI_INTERVAL, &esi_interval)) {
++			esi_interval = isns_config.ic_esi_min_interval;
++		} else
++		if (esi_interval < isns_config.ic_esi_min_interval) {
++			esi_interval = isns_config.ic_esi_min_interval;
++		} else
++		if (esi_interval > isns_config.ic_esi_max_interval) {
++			esi_interval = isns_config.ic_esi_max_interval;
++		} else {
++			esi_interval = 0;
++		}
++
++		if (esi_interval)
++			isns_object_set_uint32(current,
++					ISNS_TAG_ESI_INTERVAL, esi_interval);
++	}
++
++	/* Append it to the result list.
++	 * We do not return the key object, otherwise
++	 * we end up putting it into the response twice.
++	 */
++	if (current != st->key_obj)
++		isns_object_list_append(result, current);
++
++	/*
++	 * When a Portal is registered, the Portal attributes MAY immediately be
++	 * followed by a PGT attribute. 
++	 * [...]
++	 * When an iSCSI Storage Node is registered, the Storage Node attributes
++	 * MAY immediately be followed by a PGT attribute.
++	 */
++	if (st->tmpl == &isns_portal_template
++	 || st->tmpl == &isns_iscsi_node_template) {
++		st->pgt_next_attr = ISNS_TAG_PG_TAG;
++		st->pgt_base_object = current;
++	} else if (st->tmpl != &isns_iscsi_pg_template) {
++		st->pgt_next_attr = 0;
++		st->pgt_base_object = NULL;
++	}
++
++	isns_object_release(current);
++	return ISNS_SUCCESS;
++
++invalid_registration:
++	if (current)
++		isns_object_release(current);
++	return ISNS_INVALID_REGISTRATION;
++}
++
++/*
++ * Extract the list of objects to be registered from
++ * the list of operating attributes.
++ */
++static int
++isns_registration_get_objects(isns_simple_t *reg, isns_db_t *db,
++					isns_object_t *key_obj,
++					isns_object_list_t *result)
++{
++	struct isns_attr_list_scanner state;
++	int		status = ISNS_SUCCESS;
++
++	isns_attr_list_scanner_init(&state, key_obj, &reg->is_operating_attrs);
++	state.source = reg->is_source;
++	state.policy = reg->is_policy;
++
++	/*
++	 * 5.6.4.
++	 * The ordering of Operating Attributes in the message is
++	 * important for determining the relationships among objects
++	 * and their ownership of non-key attributes.  iSNS protocol
++	 * messages that violate these ordering rules SHALL be rejected
++	 * with the Status Code of 2 (Message Format Error).
++	 */
++	/* FIXME: Implement this check */
++
++	while (state.pos < state.orig_attrs.ial_count) {
++		status = isns_registration_get_next_object(db,
++				&state, result);
++
++		if (status)
++			break;
++	}
++
++	isns_attr_list_scanner_destroy(&state);
++	return status;
++}
++
++/*
++ * 5.6.5.1
++ * New PG objects are registered when an associated Portal or
++ * iSCSI Node object is registered.  An explicit PG object
++ * registration MAY follow a Portal or iSCSI Node object
++ * registration in a DevAttrReg message.
++ * [...]
++ * If the PGT value is not included in the Storage Node or
++ * Portal object registration, and if a PGT value was not
++ * previously registered for the relationship, then the PGT for
++ * the corresponding PG object SHALL be registered with a value
++ * of 0x00000001.
++ */
++static int
++isns_create_registration_pgs(isns_db_t *db,
++		const isns_object_list_t *list)
++{
++	unsigned int	i, num_created = 0;
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t	*obj = list->iol_data[i];
++
++		if (ISNS_IS_ISCSI_NODE(obj) || ISNS_IS_PORTAL(obj))
++			num_created += isns_create_default_pgs_for_object(db, obj);
++	}
++	return num_created;
++}
++
++static int
++isns_create_default_pgs_for_object(isns_db_t *db, isns_object_t *this)
++{
++	isns_object_template_t *match_tmpl;
++	isns_object_t	*entity;
++	unsigned int	i, num_created = 0;
++
++	if (ISNS_IS_ISCSI_NODE(this))
++		match_tmpl = &isns_portal_template;
++	else
++		match_tmpl = &isns_iscsi_node_template;
++
++	entity = isns_object_get_entity(this);
++	for (i = 0; i < entity->ie_children.iol_count; ++i) {
++		isns_object_t	*that = entity->ie_children.iol_data[i], *pg;
++
++		if (that->ie_template != match_tmpl)
++			continue;
++
++		/* Create the portal group if it does not
++		 * exist. 
++		 * Note: we do not return these implicitly
++		 * created portal groups - that's a matter
++		 * of sheer laziness. We would have to
++		 * splice these into the list in the
++		 * appropriate location, and I guess it's
++		 * not really worth the hassle.
++		 */
++		if (ISNS_IS_ISCSI_NODE(this))
++			pg = isns_create_default_portal_group(db, that, this);
++		else
++			pg = isns_create_default_portal_group(db, this, that);
++
++		/* There already is a PG linking these two
++		 * objects. */
++		if (pg == NULL)
++			continue;
++
++		isns_db_insert(db, pg);
++
++		isns_debug_message("--Created default PG:--\n");
++		isns_object_print(pg, isns_debug_message);
++
++		isns_object_release(pg);
++		num_created++;
++	}
++
++	return num_created;
++}
++
++/*
++ * Commit all changes to the DB
++ */
++static int
++isns_commit_registration(isns_db_t *db, isns_object_t *key_obj, isns_object_list_t *list)
++{
++	unsigned int		i;
++
++	/*
++	 * If there are any Portal Groups in this registration, build
++	 * the relationship handle:
++	 *
++	 * 3.4
++	 * A new PG object can only be registered by referencing
++	 * its associated iSCSI Storage Node or Portal object.
++	 * A pre-existing PG object can be modified or queried
++	 * by using its Portal Group Index as message key, or
++	 * by referencing its associated iSCSI Storage Node or
++	 * Portal object.
++	 *
++	 * Implementation note: isns_db_create_pg_relation
++	 * checks whether the referenced node and portal exist,
++	 * and belong to the same entity as the PG. If this is
++	 * not the case, NULL is returned, and no relation is
++	 * defined.
++	 */
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t *obj = list->iol_data[i];
++		isns_object_template_t *tmpl;
++
++		tmpl = obj->ie_template;
++		if (tmpl->iot_build_relation && !obj->ie_relation
++		 && !tmpl->iot_build_relation(db, obj, list)) {
++			isns_debug_protocol("Unable to build relation for new %s\n",
++					tmpl->iot_name);
++			return ISNS_INVALID_REGISTRATION;
++		}
++	}
++
++	for (i = 0; i < list->iol_count; ++i) {
++		isns_object_t *obj = list->iol_data[i];
++		isns_object_template_t *tmpl;
++
++		tmpl = obj->ie_template;
++		if (key_obj != obj && !obj->ie_container) {
++			if (!isns_object_attach(obj, key_obj)) {
++				/* This should not fail any longer */
++				isns_debug_protocol("Unable to attach %s %u to %s\n",
++					tmpl->iot_name, obj->ie_index,
++					key_obj->ie_template->iot_name);
++				return ISNS_INVALID_REGISTRATION;
++			}
++		}
++
++		if (obj->ie_state != ISNS_OBJECT_STATE_MATURE)
++			isns_db_insert(db, obj);
++	}
++
++	return ISNS_SUCCESS;
++}
++
++/*
++ * Process a registration
++ */
++int
++isns_process_registration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
++{
++	isns_object_list_t	objects = ISNS_OBJECT_LIST_INIT;
++	isns_simple_t		*reply = NULL;
++	isns_object_t		*key_obj = NULL;
++	isns_db_t		*db = srv->is_db;
++	int			status;
++	unsigned int		i;
++
++	/*
++	 * 5.6.1
++	 * For messages that change the contents of the iSNS database,
++	 * the iSNS server MUST verify that the Source Attribute
++	 * identifies either a Control Node or a Storage Node that is
++	 * a part of the Network Entity containing the added, deleted,
++	 * or modified objects.
++	 *
++	 * This check happens in isns_registration_get_key by calling
++	 * isns_policy_validate_object_access.
++	 */
++
++	/* Get the key object (usually a Network Entity) */
++	status = isns_registration_get_key(call, db, &key_obj);
++	if (status)
++		goto done;
++
++	/* Get the objects to register */
++	status = isns_registration_get_objects(call, db, key_obj, &objects);
++	if (status != ISNS_SUCCESS)
++		goto done;
++
++	/* We parsed the request alright; all semantic checks passed.
++	 * Now insert the modified/new objects.
++	 * We do this in two passes, by first committing all nodes and
++	 * portals, and then committing the portal groups.
++	 */
++	status = isns_commit_registration(db, key_obj, &objects);
++	if (status != ISNS_SUCCESS)
++		goto done;
++
++	/* The client may have registered a bunch of storage nodes,
++	 * and created an entity in the process. However, there's the
++	 * odd chance that the source node name it used was not
++	 * registered. However, we need to be able to later find
++	 * the entity it registered based on its source name.
++	 * So we implicitly create a dummy storage node with the given
++	 * source name and attach it.
++	 */
++#if 1
++	if (ISNS_IS_ENTITY(key_obj)
++	 && !isns_source_set_node(call->is_source, db)) {
++		isns_attr_list_t attrs = ISNS_ATTR_LIST_INIT;
++		isns_source_t *source = call->is_source;
++		isns_object_t *obj;
++
++		isns_attr_list_append_attr(&attrs, isns_source_attr(source));
++		isns_attr_list_append_uint32(&attrs, 
++				ISNS_TAG_ISCSI_NODE_TYPE,
++				0);
++		obj = isns_create_object(&isns_iscsi_node_template,
++				&attrs, key_obj);
++		if (obj) {
++			isns_db_insert(db, obj);
++		} else {
++			isns_warning("Unable to create dummy storage node "
++					"for source %s\n",
++					isns_source_name(source));
++		}
++		isns_attr_list_destroy(&attrs);
++	}
++#endif
++
++	/*
++	 * 5.6.5.1
++	 * New PG objects are registered when an associated Portal or
++	 * iSCSI Node object is registered.  An explicit PG object
++	 * registration MAY follow a Portal or iSCSI Node object
++	 * registration in a DevAttrReg message.
++	 * [...]
++	 * If the PGT value is not included in the Storage Node or
++	 * Portal object registration, and if a PGT value was not
++	 * previously registered for the relationship, then the PGT for
++	 * the corresponding PG object SHALL be registered with a value
++	 * of 0x00000001.
++	 */
++	isns_create_registration_pgs(db, &objects);
++
++	/* Success: create a new registration message, and
++	 * send it in our reply. */
++	reply = __isns_create_registration(srv->is_source, key_obj);
++	if (reply == NULL) {
++		status = ISNS_INTERNAL_ERROR;
++		goto done;
++	}
++
++	/* If the key object was modified (or created)
++	 * include it in the response.
++	 * We really ought to restrict ourselves to the
++	 * key attrs plus those that were modified by this
++	 * registration. But right now have no way of
++	 * finding out.
++	 */
++	if (key_obj->ie_flags & ISNS_OBJECT_DIRTY)
++		isns_registration_add_object(reply, key_obj);
++
++	for (i = 0; i < objects.iol_count; ++i) {
++		isns_registration_add_object(reply,
++				objects.iol_data[i]);
++	}
++
++
++done:
++	isns_object_list_destroy(&objects);
++	isns_object_release(key_obj);
++	*result = reply;
++	return status;
++}
++
++/*
++ * Extract the list of objects from the DevAttrReg response
++ */
++int
++isns_registration_response_get_objects(isns_simple_t *reg,
++		isns_object_list_t *result)
++{
++	return isns_simple_response_get_objects(reg, result);
++}
+diff --git a/utils/open-isns/relation.c b/utils/open-isns/relation.c
+new file mode 100644
+index 0000000..caac38b
+--- /dev/null
++++ b/utils/open-isns/relation.c
+@@ -0,0 +1,281 @@
++/*
++ * iSNS object relationships
++ *
++ * Relations are used to express a connection between two
++ * objects. Currently, two relationship types are implemented:
++ *
++ *  - portal group: this relates a storage node and a portal
++ *  - visibility: this relates a nodes nodes that share a
++ *	common discovery domain.
++ *
++ * Relation objects are nice for portals groups, but kind of
++ * awkward for DDs. A better way of expressing DD membership
++ * (which also allows for a fast visibility check) could be
++ * to store a [bit] vector of DD IDs in each storage node.
++ * A visibility check would amount to just doing the bitwise
++ * AND of two vectors, and checking for NULL. The only thing
++ * to take care of would be to make sure a DD object takes a
++ * reference on its members (this is necessary so that objects
++ * maintain their ID/name associations even when removed from
++ * the database).
++ *
++ * Aug 22 2007 - changed DD code to use bit vectors. A lot
++ *	of code in this file is now obsolete.
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include <stdarg.h>
++
++#include "isns.h"
++#include "objects.h"
++#include "util.h"
++#include "db.h"
++
++struct isns_relation_soup {
++	/* For now, use one plain list. For better
++	 * scalability, we'll need a hash table or
++	 * something similar later. */
++	isns_relation_list_t	irs_data;
++};
++
++static void	isns_relation_list_append(isns_relation_list_t *,
++			isns_relation_t *);
++static int	isns_relation_list_remove(isns_relation_list_t *,
++			isns_relation_t *);
++
++isns_relation_soup_t *
++isns_relation_soup_alloc(void)
++{
++	return isns_calloc(1, sizeof(isns_relation_soup_t));
++}
++
++void
++isns_relation_add(isns_relation_soup_t *soup,
++		isns_relation_t *rp)
++{
++	isns_relation_list_append(&soup->irs_data, rp);
++}
++
++isns_relation_t *
++isns_relation_find_edge(isns_relation_soup_t *soup,
++		const isns_object_t *left,
++		const isns_object_t *right,
++		unsigned int relation_type)
++{
++	isns_relation_list_t *list = &soup->irs_data;
++	unsigned int	i;
++
++	for (i = 0; i < list->irl_count; ++i) {
++		isns_relation_t *rp = list->irl_data[i];
++
++		if (rp->ir_type != relation_type)
++			continue;
++		if (rp->ir_subordinate[0].obj == left
++		 && rp->ir_subordinate[1].obj == right)
++			return rp;
++		if (rp->ir_subordinate[0].obj == right
++		 && rp->ir_subordinate[1].obj == left)
++			return rp;
++	}
++	return NULL;
++}
++
++void
++isns_relation_get_edge_objects(isns_relation_soup_t *soup,
++		const isns_object_t *left,
++		unsigned int relation_type,
++		isns_object_list_t *result)
++{
++	isns_relation_list_t *list = &soup->irs_data;
++	unsigned int	i;
++
++	for (i = 0; i < list->irl_count; ++i) {
++		isns_relation_t *rp = list->irl_data[i];
++
++		if (rp->ir_type != relation_type)
++			continue;
++		if (rp->ir_object == NULL)
++			continue;
++		if (rp->ir_subordinate[0].obj == left
++		 || rp->ir_subordinate[1].obj == left) {
++			isns_object_list_append(result,
++				rp->ir_object);
++		}
++	}
++}
++
++
++
++void
++isns_relation_halfspace(isns_relation_soup_t *soup,
++		const isns_object_t *left,
++		unsigned int relation_type,
++		isns_object_list_t *result)
++{
++	isns_relation_list_t *list = &soup->irs_data;
++	unsigned int	i;
++
++	for (i = 0; i < list->irl_count; ++i) {
++		isns_relation_t *rp = list->irl_data[i];
++
++		if (rp->ir_type != relation_type)
++			continue;
++		if (rp->ir_subordinate[0].obj == left) {
++			isns_object_list_append(result,
++				rp->ir_subordinate[1].obj);
++		} else
++		if (rp->ir_subordinate[1].obj == left) {
++			isns_object_list_append(result,
++				rp->ir_subordinate[0].obj);
++		}
++	}
++}
++
++int
++isns_relation_exists(isns_relation_soup_t *soup,
++		const isns_object_t *relating_object,
++		const isns_object_t *left,
++		const isns_object_t *right,
++		unsigned int relation_type)
++{
++	isns_relation_list_t *list = &soup->irs_data;
++	unsigned int	i;
++
++	for (i = 0; i < list->irl_count; ++i) {
++		isns_relation_t *rp = list->irl_data[i];
++
++		if (rp->ir_type != relation_type)
++			continue;
++		if (rp->ir_object != relating_object)
++			continue;
++		if (rp->ir_subordinate[0].obj == left
++		 && rp->ir_subordinate[1].obj == right)
++			return 1;
++		if (rp->ir_subordinate[0].obj == right
++		 && rp->ir_subordinate[1].obj == left)
++			return 1;
++	}
++	return 0;
++}
++
++isns_object_t *
++isns_relation_get_other(const isns_relation_t *rp,
++			const isns_object_t *this)
++{
++	if (rp->ir_subordinate[0].obj == this)
++		return rp->ir_subordinate[1].obj;
++	if (rp->ir_subordinate[1].obj == this)
++		return rp->ir_subordinate[0].obj;
++	return NULL;
++}
++
++void
++isns_relation_remove(isns_relation_soup_t *soup,
++		isns_relation_t *rp)
++{
++	isns_object_release(rp->ir_object);
++	rp->ir_object = NULL;
++
++	isns_relation_list_remove(&soup->irs_data, rp);
++}
++
++isns_relation_t *
++isns_create_relation(isns_object_t *relating_object,
++		unsigned int relation_type,
++		isns_object_t *subordinate_object1,
++		isns_object_t *subordinate_object2)
++{
++	isns_relation_t *rp;
++	
++	rp = isns_calloc(1, sizeof(*rp));
++	rp->ir_type = relation_type;
++	rp->ir_users = 1;
++	rp->ir_object = isns_object_get(relating_object);
++	isns_object_reference_set(&rp->ir_subordinate[0], subordinate_object1);
++	isns_object_reference_set(&rp->ir_subordinate[1], subordinate_object2);
++
++#if 0
++	if (relating_object) {
++		relating_object->ie_relation = rp;
++		rp->ir_users++;
++	}
++#endif
++
++	return rp;
++}
++
++void
++isns_relation_sever(isns_relation_t *rp)
++{
++	isns_object_release(rp->ir_object);
++	rp->ir_object = NULL;
++
++	isns_object_reference_drop(&rp->ir_subordinate[0]);
++	isns_object_reference_drop(&rp->ir_subordinate[1]);
++}
++
++void
++isns_relation_release(isns_relation_t *rp)
++{
++	if (--(rp->ir_users))
++		return;
++
++	isns_relation_sever(rp);
++	isns_free(rp);
++}
++
++/*
++ * Check whether the relation references two dead/limbo objects.
++ * This is used for dead PG removal.
++ */
++int
++isns_relation_is_dead(const isns_relation_t *rel)
++{
++	isns_object_t	*left, *right;
++
++	left = rel->ir_subordinate[0].obj;
++	right = rel->ir_subordinate[1].obj;
++	if ((left->ie_flags & ISNS_OBJECT_DEAD)
++	 && (right->ie_flags & ISNS_OBJECT_DEAD))
++		return 1;
++
++	return 0;
++}
++
++void
++isns_relation_list_append(isns_relation_list_t *list,
++			isns_relation_t *rp)
++{
++	if ((list->irl_count % 128) == 0) {
++		list->irl_data = isns_realloc(list->irl_data,
++				(list->irl_count + 128) * sizeof(void *));
++		if (list->irl_data == NULL)
++			isns_fatal("out of memory!\n");
++	}
++
++	list->irl_data[list->irl_count++] = rp;
++	rp->ir_users++;
++}
++
++int
++isns_relation_list_remove(isns_relation_list_t *list,
++			isns_relation_t *rp)
++{
++	unsigned int	i, count = list->irl_count;
++
++	for (i = 0; i < count; ++i) {
++		if (list->irl_data[i] != rp)
++			continue;
++		if (i < count - 1)
++			list->irl_data[i] = list->irl_data[count-1];
++		isns_relation_release(rp);
++		list->irl_count -= 1;
++		return 1;
++	}
++
++	return 0;
++}
+diff --git a/utils/open-isns/scn.c b/utils/open-isns/scn.c
+new file mode 100644
+index 0000000..51fcba3
+--- /dev/null
++++ b/utils/open-isns/scn.c
+@@ -0,0 +1,926 @@
++/*
++ * Handle SCN registration/deregistration/events
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include "isns.h"
++#include "attrs.h"
++#include "objects.h"
++#include "message.h"
++#include "security.h"
++#include "util.h"
++#include "db.h"
++
++typedef struct isns_scn isns_scn_t;
++typedef struct isns_scn_funnel isns_scn_funnel_t;
++
++struct isns_scn {
++	isns_scn_t *		scn_next;
++	char *			scn_name;
++	isns_object_t *		scn_entity;
++	isns_object_t *		scn_owner;
++	isns_attr_t *		scn_attr;
++
++	isns_simple_t *		scn_message;
++	isns_simple_t *		scn_pending;
++	unsigned int		scn_retries;
++	time_t			scn_timeout;
++	uint16_t		scn_xid;
++
++	time_t			scn_last_update;
++	isns_scn_funnel_t *	scn_current_funnel;
++	isns_scn_funnel_t *	scn_funnels;
++};
++
++struct isns_scn_funnel {
++	isns_scn_funnel_t *	scn_next;
++	isns_portal_info_t	scn_portal;
++	isns_socket_t *		scn_socket;
++	unsigned int		scn_bad;
++};
++
++static isns_server_t *		isns_scn_server = NULL;
++static isns_scn_t *		isns_scn_list;
++
++static isns_scn_t *		isns_scn_create_scn(isns_object_t *, uint32_t, isns_db_t *);
++static void			isns_scn_delete_scn(isns_object_t *);
++static isns_scn_t *		isns_scn_setup(isns_scn_t *, isns_object_t *);
++static void			isns_scn_callback(const isns_db_event_t *, void *);
++static void			isns_scn_free(isns_scn_t *);
++
++/*
++ * Initialize SCN machinery
++ */
++void
++isns_scn_init(isns_server_t *srv)
++{
++	isns_db_t	*db = srv->is_db;
++	isns_object_list_t nodes = ISNS_OBJECT_LIST_INIT;
++	isns_scn_t	**tail;
++	unsigned int	i;
++
++	isns_scn_server = srv;
++	isns_register_callback(isns_scn_callback, db);
++
++	/* Recover SCN state. */
++	isns_db_gang_lookup(db, &isns_iscsi_node_template, NULL, &nodes);
++#ifdef notyet
++	isns_db_gang_lookup(db, &isns_fc_node_template, NULL, &nodes);
++#endif
++
++	tail = &isns_scn_list;
++	for (i = 0; i < nodes.iol_count; ++i) {
++		isns_object_t	*node = nodes.iol_data[i];
++		isns_scn_t	*scn;
++
++		if (!node->ie_scn_mask)
++			continue;
++
++		isns_debug_state("Recovering SCN state for %s %u\n",
++				node->ie_template->iot_name,
++				node->ie_index);
++		scn = isns_scn_setup(NULL, node);
++		if (scn) {
++			*tail = scn;
++			tail = &scn->scn_next;
++		}
++	}
++}
++
++/*
++ * Support for SCNRegister calls
++ */
++isns_simple_t *
++isns_create_scn_registration2(isns_client_t *clnt, unsigned int bitmap, isns_source_t *source)
++{
++	isns_simple_t	*call;
++
++	if (!source)
++		source = clnt->ic_source;
++	call = isns_simple_create(ISNS_SCN_REGISTER, source, NULL);
++	if (call) {
++		isns_attr_list_append_attr(&call->is_message_attrs,
++				isns_source_attr(source));
++		isns_attr_list_append_uint32(&call->is_operating_attrs,
++				ISNS_TAG_ISCSI_SCN_BITMAP,
++				bitmap);
++	}
++	return call;
++}
++
++isns_simple_t *
++isns_create_scn_registration(isns_client_t *clnt, unsigned int bitmap)
++{
++	return isns_create_scn_registration2(clnt, bitmap, clnt->ic_source);
++}
++
++/*
++ * Create an SCN
++ */
++isns_simple_t *
++isns_create_scn(isns_source_t *source, isns_attr_t *nodeattr, isns_attr_t *tsattr)
++{
++	isns_simple_t	*call;
++
++	call = isns_simple_create(ISNS_STATE_CHANGE_NOTIFICATION, source, NULL);
++	if (call && nodeattr)
++		isns_attr_list_append_attr(&call->is_message_attrs, nodeattr);
++	if (call && tsattr)
++		isns_attr_list_append_attr(&call->is_message_attrs, tsattr);
++	return call;
++}
++
++static void
++isns_scn_add_event(isns_simple_t *call, uint32_t scn_bits,
++			const isns_object_t *obj,
++			const isns_object_t *dd)
++{
++	isns_attr_list_t	*attrs = &call->is_message_attrs;
++
++	isns_attr_list_append_uint32(attrs,
++			ISNS_TAG_ISCSI_SCN_BITMAP,
++			scn_bits);
++	isns_object_extract_keys(obj, attrs);
++	if (dd)
++		isns_object_extract_keys(dd, attrs);
++}
++
++/*
++ * Process a SCN registration
++ */
++int
++isns_process_scn_register(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
++{
++	isns_attr_list_t *keys = &call->is_message_attrs;
++	isns_attr_list_t *attrs = &call->is_operating_attrs;
++	isns_db_t	*db = srv->is_db;
++	isns_attr_t	*attr;
++	isns_object_t	*node = NULL;
++	uint32_t	scn_bitmap;
++	isns_scn_t	*scn;
++	int		status = ISNS_SUCCESS;
++
++	/*
++	 * 5.6.5.5
++	 * The SCNReg request PDU Payload contains a Source Attribute, a Message
++	 * Key Attribute, and an Operating Attribute.  Valid Message Key
++	 * Attributes for a SCNReg are shown below:
++	 *
++	 * Valid Message Key Attributes for SCNReg
++	 * ---------------------------------------
++	 *  iSCSI Name
++	 *  FC Port Name WWPN
++	 */
++	if (keys->ial_count != 1 || attrs->ial_count != 1)
++		return ISNS_SCN_REGISTRATION_REJECTED;
++
++	attr = keys->ial_data[0];
++	if (attr->ia_tag_id != ISNS_TAG_ISCSI_NAME &&
++	    attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN)
++		return ISNS_SCN_REGISTRATION_REJECTED;
++
++	/* Look up the storage node for this source. If it does
++	 * not exist, reject the message. */
++	node = isns_db_lookup(db, NULL, keys);
++	if (node == NULL)
++		return ISNS_SOURCE_UNKNOWN;
++
++	/*
++	 * Policy: verify that the client is permitted
++	 * to access this entity.
++	 *
++	 * This includes
++	 *  -	the client node must be the object owner,
++	 *	or a control node.
++	 *  -	the policy must allow monitoring of
++	 *	this object type.
++	 */
++	if (!isns_policy_validate_object_access(call->is_policy,
++				call->is_source,
++				node, call->is_function))
++		goto unauthorized;
++
++	/*
++	 * 5.6.5.5
++	 * The SCN Bitmap is the only operating attribute of this message
++	 * [...]
++	 * Control Nodes MAY conduct registrations for management SCNs;
++	 * iSNS clients that are not supporting Control Nodes MUST NOT
++	 * conduct registrations for management SCNs.
++	 *
++	 * Implementer's note: for iFCP sources, we should check for
++	 * ISNS_TAG_IFCP_SCN_BITMAP.
++	 */
++	attr = attrs->ial_data[0];
++	if (attr->ia_tag_id != ISNS_TAG_ISCSI_SCN_BITMAP
++	 || !ISNS_ATTR_IS_UINT32(attr))
++		goto rejected;
++
++	scn_bitmap = attr->ia_value.iv_uint32;
++	if (!isns_policy_validate_scn_bitmap(call->is_policy, scn_bitmap))
++		goto unauthorized;
++
++	/*
++	 * 5.6.5.5
++	 * If no SCN Port fields of any Portals of the Storage Node are
++	 * registered to receive SCN messages, then the SCNReg message SHALL
++	 * be rejected with Status Code 17 (SCN Registration Rejected).
++	 */
++	if (!(scn = isns_scn_create_scn(node, scn_bitmap, db)))
++		goto rejected;
++
++	*result = isns_simple_create(ISNS_SCN_REGISTER, srv->is_source, NULL);
++	status = ISNS_SUCCESS;
++
++out:
++	if (node)
++		isns_object_release(node);
++
++	return status;
++
++rejected:
++	status = ISNS_SCN_REGISTRATION_REJECTED;
++	goto out;
++
++unauthorized:
++	status = ISNS_SOURCE_UNAUTHORIZED;
++	goto out;
++}
++
++/*
++ * Process a SCNDereg message
++ */
++int
++isns_process_scn_deregistration(isns_server_t *srv, isns_simple_t *call, isns_simple_t **result)
++{
++	isns_attr_list_t *keys = &call->is_message_attrs;
++	isns_db_t	*db = srv->is_db;
++	isns_attr_t	*attr;
++	isns_object_t	*node = NULL;
++	int		status = ISNS_SUCCESS;
++
++	/*
++	 * 5.6.5.6
++	 * The SCNDereg request message PDU Payload contains a Source Attribute
++	 * and Message Key Attribute(s).  Valid Message Key Attributes for a
++	 * SCNDereg are shown below:
++	 *
++	 *	  Valid Message Key Attributes for SCNDereg
++	 *	  -----------------------------------------
++	 *	   iSCSI Name
++	 *	   FC Port Name WWPN
++	 *
++	 * There are no Operating Attributes in the SCNDereg message.
++	 */
++
++	if (keys->ial_count != 1)
++		return ISNS_SCN_REGISTRATION_REJECTED;
++
++	attr = keys->ial_data[0];
++	if (attr->ia_tag_id != ISNS_TAG_ISCSI_NAME &&
++	    attr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN)
++		return ISNS_SCN_REGISTRATION_REJECTED;
++
++	/* Look up the storage node for this source. If it does
++	 * not exist, reject the message. */
++	node = isns_db_lookup(db, NULL, keys);
++	if (node == NULL)
++		return ISNS_SUCCESS;
++
++	/*
++	 * Policy: verify that the client is permitted
++	 * to access this entity.
++	 *
++	 * This includes
++	 *  -	the client node must be the object owner,
++	 *	or a control node.
++	 *  -	the policy must allow monitoring of
++	 *	this object type.
++	 */
++	if (!isns_policy_validate_object_access(call->is_policy,
++				call->is_source,
++				node, call->is_function))
++		goto unauthorized;
++
++	isns_object_set_scn_mask(node, 0);
++	isns_scn_delete_scn(node);
++
++	*result = isns_simple_create(ISNS_SCN_DEREGISTER, srv->is_source, NULL);
++	status = ISNS_SUCCESS;
++
++out:
++	if (node)
++		isns_object_release(node);
++
++	return status;
++
++unauthorized:
++	status = ISNS_SOURCE_UNAUTHORIZED;
++	goto out;
++}
++
++/*
++ * Set up the SCN object.
++ */
++static isns_scn_t *
++isns_scn_setup(isns_scn_t *scn, isns_object_t *node)
++{
++	isns_object_list_t portals = ISNS_OBJECT_LIST_INIT;
++	isns_object_t	*entity;
++	unsigned int	i;
++
++	entity = isns_object_get_entity(node);
++	if (entity == NULL
++	 || !isns_object_find_descendants(entity,
++			 &isns_portal_template, NULL, &portals))
++		return NULL;
++
++	for (i = 0; i < portals.iol_count; ++i) {
++		isns_object_t	*portal = portals.iol_data[i];
++		isns_portal_info_t info;
++		isns_scn_funnel_t *funnel;
++
++		/* Extract address and SCN port from portal */
++		if (!isns_portal_from_object(&info,
++				ISNS_TAG_PORTAL_IP_ADDRESS,
++				ISNS_TAG_SCN_PORT,
++				portal))
++			continue;
++
++		/* We know where to send our notifications! */
++		if (scn == NULL) {
++			isns_attr_t	*attr;
++
++			if (!isns_object_get_attr(node, ISNS_TAG_ISCSI_NAME, &attr)
++			 && !isns_object_get_attr(node, ISNS_TAG_FC_PORT_NAME_WWPN, &attr)) {
++				isns_error("Attempt to set up SCN for strange node type\n");
++				return NULL;
++			}
++
++			scn = isns_calloc(1, sizeof(*scn));
++			scn->scn_entity = isns_object_get(entity);
++			scn->scn_owner = isns_object_get(node);
++			scn->scn_attr = isns_attr_get(attr);
++			scn->scn_name = isns_strdup(attr->ia_value.iv_string);
++		}
++
++		funnel = isns_calloc(1, sizeof(*funnel));
++		funnel->scn_portal = info;
++		funnel->scn_next = scn->scn_funnels;
++		scn->scn_funnels = funnel;
++	}
++
++	isns_object_list_destroy(&portals);
++	return scn;
++}
++
++/*
++ * See if an SCN object exists for the given target;
++ * if it doesn't, then create one.
++ */
++static isns_scn_t *
++isns_scn_create_scn(isns_object_t *node, uint32_t bitmap, isns_db_t *db)
++{
++	isns_scn_t	*scn;
++
++	for (scn = isns_scn_list; scn; scn = scn->scn_next) {
++		if (scn->scn_owner == node)
++			goto done;
++	}
++
++	/* Not found - create it */
++	scn = isns_scn_setup(NULL, node);
++	if (scn == NULL)
++		return NULL;
++
++	scn->scn_next = isns_scn_list;
++	isns_scn_list = scn;
++
++done:
++	/* We're all set - update the bitmap */
++	isns_object_set_scn_mask(node, bitmap);
++	return scn;
++}
++
++static void
++isns_scn_delete_scn(isns_object_t *node)
++{
++	isns_scn_t	*scn, **pos;
++
++	pos = &isns_scn_list;
++	while ((scn = *pos) != NULL) {
++		if (scn->scn_owner == node) {
++			isns_debug_scn("Deregistering SCN for node %u\n",
++					node->ie_index);
++			*pos = scn->scn_next;
++			isns_scn_free(scn);
++			return;
++		}
++		pos = &scn->scn_next;
++	}
++}
++
++static void
++isns_scn_release_funnels(isns_scn_t *scn)
++{
++	isns_scn_funnel_t *funnel;
++
++	while ((funnel = scn->scn_funnels) != NULL) {
++		scn->scn_funnels = funnel->scn_next;
++		if (funnel->scn_socket)
++			isns_socket_free(funnel->scn_socket);
++		isns_free(funnel);
++	}
++}
++
++static void
++isns_scn_free(isns_scn_t *scn)
++{
++	isns_scn_release_funnels(scn);
++	isns_object_release(scn->scn_owner);
++	isns_object_release(scn->scn_entity);
++	isns_attr_release(scn->scn_attr);
++	isns_free(scn->scn_name);
++	isns_free(scn);
++}
++
++/*
++ * Check whether we should send an event to the target
++ */
++static inline int
++isns_scn_match(isns_scn_t *scn, uint32_t event,
++		const isns_object_t *node,
++		uint32_t node_type)
++{
++	if (event == 0)
++		return 0;
++
++	if (node->ie_scn_mask & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK)
++		return event | ISNS_SCN_MANAGEMENT_REGISTRATION_MASK;
++
++#if 0
++	/* This is a normal (non-control) node. Check whether the object
++	 * is in the scope of this client. */
++	if (!isns_object_in_scope(scn->scn_owner, node))
++		return 0;
++#endif
++
++	if (node->ie_scn_mask & ISNS_SCN_TARGET_AND_SELF_ONLY_MASK) {
++		if (node != scn->scn_owner && !(node_type & ISNS_ISCSI_TARGET_MASK))
++			return 0;
++	}
++	if (node->ie_scn_mask & ISNS_SCN_INITIATOR_AND_SELF_ONLY_MASK) {
++		if (node != scn->scn_owner && !(node_type & ISNS_ISCSI_INITIATOR_MASK))
++			return 0;
++	}
++
++	return event;
++}
++
++/*
++ * Helper to create time stamp attr
++ */
++static isns_attr_t *
++isns_create_timestamp_attr(void)
++{
++	isns_value_t	value = ISNS_VALUE_INIT(uint64, time(NULL));
++
++	return isns_attr_alloc(ISNS_TAG_TIMESTAMP, NULL, &value);
++}
++
++/*
++ * This function is invoked whenever someone changes the
++ * database.
++ *
++ * SCNs are another area where the RFC is fabulously wishy washy.
++ * It is not entirely clear when DD/DDS information should be
++ * included in a management SCN - one *reasonable* interpretation
++ * would be that this happens for DDReg/DDDereg/DDSReg/DDSDereg
++ * events only. But some sections make it sound as if DD
++ * information is included for all management SCNs.
++ */
++void
++isns_scn_callback(const isns_db_event_t *ev, void *ptr)
++{
++	isns_object_t	*obj = ev->ie_object;
++	isns_scn_t	*scn, **pos;
++	isns_attr_t	*timestamp;
++	uint32_t	node_type;
++
++	/* Never send out notifications for policy objects and the like. */
++	if (obj->ie_flags & ISNS_OBJECT_PRIVATE)
++		return;
++
++	/* When an entity is nuked, remove all SCNs to nodes
++	 * that registered from there */
++	if (ISNS_IS_ENTITY(obj) && (ev->ie_bits & ISNS_SCN_OBJECT_REMOVED_MASK)) {
++		pos = &isns_scn_list;
++		while ((scn = *pos) != NULL) {
++			if (scn->scn_entity != obj) {
++				pos = &scn->scn_next;
++				continue;
++			}
++			isns_debug_scn("Deleting SCN registration for %s\n",
++					scn->scn_name);
++			*pos = scn->scn_next;
++			isns_scn_free(scn);
++		}
++		return;
++	}
++
++	/* For now we handle iSCSI nodes only. Maybe later we'll
++	 * do iFC nodes as well. */
++	if (!ISNS_IS_ISCSI_NODE(obj))
++		return;
++	if (!isns_object_get_uint32(obj, ISNS_TAG_ISCSI_NODE_TYPE, &node_type))
++		return;
++
++	if (ev->ie_recipient) {
++		isns_object_t *dst = ev->ie_recipient;
++
++		isns_debug_scn("SCN unicast <%s %u, %s> -> %s %u\n",
++				obj->ie_template->iot_name, obj->ie_index,
++				isns_event_string(ev->ie_bits),
++				dst->ie_template->iot_name, dst->ie_index);
++	} else {
++		isns_debug_scn("SCN multicast <%s %u, %s>\n",
++				obj->ie_template->iot_name, obj->ie_index,
++				isns_event_string(ev->ie_bits));
++	}
++	timestamp = isns_create_timestamp_attr();
++
++	pos = &isns_scn_list;
++	while ((scn = *pos) != NULL) {
++		unsigned int	scn_bits, management;
++		isns_object_t	*recipient, *dd = NULL;
++		isns_simple_t	*call;
++
++		recipient = scn->scn_owner;
++
++		/* Check if the node has gone away completely. */
++		if (recipient->ie_scn_mask == 0) {
++			*pos = scn->scn_next;
++			isns_scn_free(scn);
++			continue;
++		}
++
++		if (recipient->ie_container == NULL) {
++			isns_warning("Internal bug - SCN recipient without container\n");
++			/* Clear the bitmask and loop over - this will remove it */
++			recipient->ie_scn_mask = 0;
++			continue;
++		}
++
++		/* See if portals were added/removed.
++		 * This does not catch updates that modified *just*
++		 * the SCN port */
++		if (recipient->ie_container->ie_mtime != scn->scn_last_update) {
++			/* Rebuild the list of SCN portals */
++			isns_scn_release_funnels(scn);
++			scn->scn_last_update = 0;
++		}
++		pos = &scn->scn_next;
++
++		/* Check for unicast events (triggered for DD addition/removal).
++		 * For unicast events, we do not mask the SCN bits, so that
++		 * clients who have registered for non-management events
++		 * will see the membership events for their DDs nevertheless. */
++		if (ev->ie_recipient == NULL) {
++			scn_bits = ev->ie_bits & recipient->ie_scn_mask;
++			if (scn_bits == 0)
++				continue;
++			/* Management SCNs should not be delivered to nodes
++			 * that have not registered for them. */
++			if ((ev->ie_bits & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK)
++			 && !(recipient->ie_scn_mask & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK))
++				continue;
++		} else if (recipient == ev->ie_recipient) {
++			scn_bits = ev->ie_bits;
++		} else {
++			/* No match, skip this recipient */
++			continue;
++		}
++
++		if (scn->scn_last_update == 0) {
++			scn->scn_last_update = recipient->ie_container->ie_mtime;
++			isns_scn_setup(scn, recipient);
++		}
++
++		/* We check for SCN capable portals when processing
++		 * the SCN registration. But the portals may go away
++		 * in the meantime. */
++		if (scn->scn_funnels == NULL)
++			continue;
++
++		/* Check SCN bitmask. This will modify the event bits. */
++		scn_bits = isns_scn_match(scn, scn_bits, obj, node_type);
++		if (scn_bits == 0)
++			continue;
++		management = !!(scn_bits & ISNS_SCN_MANAGEMENT_REGISTRATION_MASK);
++
++		/*
++		 * 2.2.3
++		 * A regular SCN registration indicates that the
++		 * Discovery Domain Service SHALL be used to control the
++		 * distribution of SCN messages.  Receipt of regular
++		 * SCNs is limited to the discovery domains in which
++		 * the SCN-triggering event takes place.  Regular SCNs
++		 * do not contain information about discovery domains.
++		 *
++		 * Implementer's note: We override check for unicast events.
++		 * The reason is that DDDereg will sever the
++		 * relationship, and we would never send an SCN for that
++		 * event.
++		 */
++		if (!management && !ev->ie_recipient) {
++			if (!isns_object_test_visibility(obj, recipient))
++				continue;
++		}
++
++		isns_debug_scn("preparing to send SCN to %s\n",
++				scn->scn_name);
++
++		if ((call = scn->scn_message) == NULL) {
++			call = isns_create_scn(isns_scn_server->is_source,
++					scn->scn_attr,
++					timestamp);
++			if (call == NULL)
++				continue;
++			scn->scn_message = call;
++		}
++
++		/*
++		 * If the SCN is a Management SCN, then the SCN message
++		 * SHALL also list the DD_ID and/or DDS_ID of the
++		 * Discovery Domains and Discovery Domain Sets (if any)
++		 * that caused the change in state for that Storage Node.
++		 * These additional attributes (i.e., DD_ID and/or DDS_ID)
++		 * shall immediately follow the iSCSI Name or FC Port
++		 * Name and precede the next SCN bitmap for the next
++		 * notification message (if any).
++		 */
++		if (management && ev->ie_trigger)
++			dd = ev->ie_trigger;
++
++		isns_scn_add_event(call, scn_bits, obj, dd);
++
++	}
++
++	isns_attr_release(timestamp);
++}
++
++/*
++ * Obtain a socket to talk to this guy.
++ * Not entirely trivial - this can be both an established
++ * (incoming) connection, or one that we should establish.
++ *
++ * Note, we do not support transmission on the incoming
++ * connection yet.
++ */
++static isns_socket_t *
++isns_scn_get_socket(isns_scn_t *scn)
++{
++	isns_scn_funnel_t *f, *best = NULL;
++	isns_socket_t	*sock;
++	unsigned int	worst = 0, loops = 0, nfunnels;
++
++	/* Keep it simple for now */
++	if ((f = scn->scn_current_funnel) != NULL && f->scn_socket) {
++		if (!f->scn_bad)
++			return f->scn_socket;
++		/* Oops, we've seen timeouts on this socket. */
++		isns_socket_free(f->scn_socket);
++		f->scn_socket = NULL;
++	}
++
++again:
++	nfunnels = 0;
++	for (f = scn->scn_funnels; f; f = f->scn_next) {
++		unsigned int	badness = f->scn_bad;
++
++		if (!best || badness < best->scn_bad)
++			best = f;
++		if (badness > worst)
++			worst = badness;
++		nfunnels++;
++	}
++
++	if (!best)
++		return NULL;
++
++	sock = isns_connect_to_portal(&best->scn_portal);
++	if (sock == NULL) {
++		/* Make sure we try each funnel exactly once */
++		best->scn_bad = worst + 1;
++		if (++loops < nfunnels)
++			goto again;
++		return NULL;
++	}
++
++	/* Set the security context */
++	isns_socket_set_security_ctx(sock,
++			isns_default_security_context(1));
++
++	isns_debug_scn("SCN: %s using portal %s\n",
++			scn->scn_name,
++			isns_portal_string(&best->scn_portal));
++	scn->scn_current_funnel = best;
++	best->scn_socket = sock;
++	return sock;
++}
++
++/*
++ * This is the callback function invoked when the SCN message reply
++ * comes in, or when the message timed out.
++ */
++static void
++isns_process_scn_response(uint32_t xid, int status, isns_simple_t *msg)
++{
++	isns_scn_t	*scn;
++
++	if (msg == NULL) {
++		isns_debug_scn("SCN timed out\n");
++		return;
++	}
++
++	isns_debug_scn("Received an SCN response\n");
++	for (scn = isns_scn_list; scn; scn = scn->scn_next) {
++		if (scn->scn_pending && scn->scn_xid == xid) {
++			isns_debug_scn("SCN: %s acknowledged notification\n",
++					scn->scn_name);
++			isns_simple_free(scn->scn_pending);
++			scn->scn_pending = NULL;
++
++			if (scn->scn_current_funnel)
++				scn->scn_current_funnel->scn_bad = 0;
++		}
++	}
++}
++/*
++ * Transmit all pending SCN messages
++ *
++ * 2.9.2
++ * If a Network Entity has multiple Portals with registered SCN UDP Ports,
++ * then SCN messages SHALL be delivered to each Portal registered to
++ * receive such messages.
++ *
++ * FIXME: we should make this timer based just as the ESI code.
++ */
++time_t
++isns_scn_transmit_all(void)
++{
++	time_t		now = time(NULL), next_timeout;
++	isns_scn_t	*scn;
++
++	for (scn = isns_scn_list; scn; scn = scn->scn_next) {
++		isns_simple_t	*call;
++		isns_socket_t	*sock;
++
++		/* We do not allow more than one outstanding
++		 * notification for now. */
++		if ((call = scn->scn_pending) != NULL) {
++			if (scn->scn_timeout > now)
++				continue;
++			scn->scn_current_funnel->scn_bad++;
++			if (--(scn->scn_retries))
++				goto retry;
++			isns_warning("SCN for %s timed out\n",
++					scn->scn_name);
++			isns_simple_free(call);
++			scn->scn_pending = NULL;
++		}
++
++		if ((call = scn->scn_message) == NULL)
++			continue;
++
++		isns_debug_scn("SCN: transmit pending message for %s\n",
++				scn->scn_name);
++		scn->scn_retries = isns_config.ic_scn_retries;
++		scn->scn_pending = call;
++		scn->scn_message = NULL;
++
++retry:
++		if ((sock = isns_scn_get_socket(scn)) == NULL) {
++			/* Sorry, no can do. */
++			isns_warning("SCN for %s dropped - no portal\n",
++					scn->scn_name);
++			scn->scn_pending = NULL;
++			isns_simple_free(call);
++			continue;
++		}
++
++		isns_simple_transmit(sock, call, NULL,
++				isns_config.ic_scn_timeout,
++				isns_process_scn_response);
++		scn->scn_xid = call->is_xid;
++		scn->scn_timeout = now + isns_config.ic_scn_timeout;
++	}
++
++	next_timeout = now + 3600;
++	for (scn = isns_scn_list; scn; scn = scn->scn_next) {
++		if (scn->scn_pending && scn->scn_timeout < next_timeout)
++			next_timeout = scn->scn_timeout;
++	}
++
++	return next_timeout;
++}
++
++/*
++ * Process an incoming State Change Notification
++ */
++int
++isns_process_scn(isns_server_t *srv, isns_simple_t *call, isns_simple_t **reply)
++{
++	isns_attr_list_t *list = &call->is_message_attrs;
++	isns_attr_t	*dstattr, *tsattr;
++	const char	*dst_name;
++	unsigned int	i;
++
++	/* The first attribute is the destination, and should match
++	 * our source name. Don't bother checking. The second is the
++	 * time stamp. 
++	 */
++	if (list->ial_count < 2)
++		goto rejected;
++
++	dstattr = list->ial_data[0];
++	if (dstattr->ia_tag_id != ISNS_TAG_ISCSI_NAME
++	 && dstattr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN)
++		goto rejected;
++	if (!ISNS_ATTR_IS_STRING(dstattr))
++		goto rejected;
++	dst_name = dstattr->ia_value.iv_string;
++
++	tsattr = list->ial_data[1];
++	if (tsattr->ia_tag_id != ISNS_TAG_TIMESTAMP)
++		return ISNS_SCN_EVENT_REJECTED;
++
++	for (i = 2; i < list->ial_count; ) {
++		isns_object_template_t *tmpl;
++		isns_attr_t	*bmattr, *srcattr;
++		const char	*node_name;
++		uint32_t	bitmap;
++
++		if (i + 1 >= list->ial_count)
++			goto rejected;
++
++		bmattr = list->ial_data[i++];
++		srcattr = list->ial_data[i++];
++
++		/* Validate that bitmap and node type match */
++		switch (bmattr->ia_tag_id) {
++		case ISNS_TAG_ISCSI_SCN_BITMAP:
++			if (srcattr->ia_tag_id != ISNS_TAG_ISCSI_NAME)
++				goto rejected;
++			tmpl = &isns_iscsi_node_template;
++			break;
++
++		case ISNS_TAG_IFCP_SCN_BITMAP:
++			if (srcattr->ia_tag_id != ISNS_TAG_FC_PORT_NAME_WWPN)
++				goto rejected;
++			tmpl = &isns_fc_port_template;
++			break;
++
++		default:
++			goto rejected;
++		}
++
++		/* Skip over and DD_ID or DDS_ID attrs */
++		while (i < list->ial_count) {
++			isns_attr_t *ddattr = list->ial_data[i];
++
++			if (ddattr->ia_tag_id == ISNS_TAG_ISCSI_SCN_BITMAP
++			 || ddattr->ia_tag_id == ISNS_TAG_IFCP_SCN_BITMAP)
++				break;
++			++i;
++		}
++
++		if (!ISNS_ATTR_IS_UINT32(bmattr))
++			goto rejected;
++		bitmap = bmattr->ia_value.iv_uint32;
++
++		if (!ISNS_ATTR_IS_STRING(srcattr))
++			goto rejected;
++		node_name = srcattr->ia_value.iv_string;
++
++		if (srv->is_scn_callback)
++			srv->is_scn_callback(srv->is_db, bitmap, tmpl, node_name, dst_name);
++	}
++
++	/*
++	 * 5.7.5.8.  SCN Response (SCNRsp)
++	 * The SCNRsp response contains the SCN Destination Attribute
++	 * representing the Node identifier that received the SCN.
++	 */
++	*reply = isns_create_scn(srv->is_source,
++			list->ial_data[0],
++			NULL);
++	return ISNS_SUCCESS;
++
++rejected:
++	return ISNS_SCN_EVENT_REJECTED;
++}
+diff --git a/utils/open-isns/scope.c b/utils/open-isns/scope.c
+new file mode 100644
+index 0000000..9ee7f9a
+--- /dev/null
++++ b/utils/open-isns/scope.c
+@@ -0,0 +1,513 @@
++/*
++ * Handle object visibility and scope.
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include "isns.h"
++#include "attrs.h"
++#include "objects.h"
++#include "message.h"
++#include "security.h"
++#include "util.h"
++#include "db.h"
++
++struct isns_scope {
++	isns_db_t *			ic_db;
++	unsigned int			ic_users;
++	isns_object_t *			ic_source_node;
++
++	isns_object_template_t *	ic_query_class;
++
++	isns_object_list_t		ic_dd_nodes;
++	isns_object_list_t		ic_dd_portals;
++	isns_object_list_t		ic_objects;
++};
++
++static int	__isns_scope_collect_dd(uint32_t, void *);
++
++/*
++ * Allocate an empty scope
++ */
++isns_scope_t *
++isns_scope_alloc(isns_db_t *db)
++{
++	isns_scope_t *scope;
++
++	scope = isns_calloc(1, sizeof(*scope));
++
++	scope->ic_db = db;
++	scope->ic_users = 1;
++	return scope;
++}
++
++isns_scope_t *
++isns_scope_get(isns_scope_t *scope)
++{
++	if (scope) {
++		isns_assert(scope->ic_users);
++		scope->ic_users++;
++	}
++	return scope;
++}
++
++void
++isns_scope_release(isns_scope_t *scope)
++{
++	if (!scope)
++		return;
++
++	isns_assert(scope->ic_users);
++	if (--(scope->ic_users))
++		return;
++
++	isns_object_release(scope->ic_source_node);
++	isns_object_list_destroy(&scope->ic_dd_nodes);
++	isns_object_list_destroy(&scope->ic_dd_portals);
++	isns_object_list_destroy(&scope->ic_objects);
++	isns_free(scope);
++}
++
++/*
++ * Get the scope for this operation
++ */
++isns_scope_t *
++isns_scope_for_call(isns_db_t *db, const isns_simple_t *call)
++{
++	isns_source_t	*source = call->is_source;
++	isns_object_t	*node;
++	isns_scope_t	*scope;
++	uint32_t	node_type;
++
++	/* FIXME use source->is_node and source->is_node_type */
++
++	/* When we get here, we already know that the client
++	 * represents the specified source node. */
++	node = isns_db_lookup_source_node(db, source);
++
++	/* Allow unknown nodes to query the DB */
++	if (node == NULL) {
++		node = isns_create_storage_node2(source, 0, NULL);
++		if (node == NULL)
++			return NULL;
++		source->is_untrusted = 1;
++	}
++
++	if (isns_object_get_uint32(node, ISNS_TAG_ISCSI_NODE_TYPE, &node_type)
++	 && (node_type & ISNS_ISCSI_CONTROL_MASK)) {
++		isns_object_release(node);
++		return isns_scope_get(db->id_global_scope);
++	}
++
++	scope = isns_scope_alloc(db);
++	scope->ic_source_node = node;
++
++	{
++		isns_object_list_t members = ISNS_OBJECT_LIST_INIT;
++		unsigned int	i;
++
++		isns_object_get_visible(node, db, &members);
++		isns_object_list_uniq(&members);
++
++		/* If the node is not a member of any DD, allow it
++		 * to at least talk to itself. */
++		if (members.iol_count == 0)
++			isns_object_list_append(&members, node);
++
++		/* Sort DD members into nodes and portals */
++		for (i = 0; i < members.iol_count; ++i) {
++			isns_object_t *obj = members.iol_data[i];
++
++			if (obj->ie_state != ISNS_OBJECT_STATE_MATURE)
++				continue;
++			if (!isns_policy_validate_object_access(call->is_policy,
++						source, obj,
++						call->is_function))
++				continue;
++			if (ISNS_IS_ISCSI_NODE(obj))
++				isns_object_list_append(&scope->ic_dd_nodes, obj);
++			else
++			if (ISNS_IS_PORTAL(obj))
++				isns_object_list_append(&scope->ic_dd_portals, obj);
++		}
++		isns_object_list_destroy(&members);
++	}
++
++	return scope;
++}
++
++/*
++ * Add an object to a scope
++ */
++void
++isns_scope_add(isns_scope_t *scope, isns_object_t *obj)
++{
++	isns_object_list_append(&scope->ic_objects, obj);
++}
++
++int
++isns_scope_remove(isns_scope_t *scope, isns_object_t *obj)
++{
++	return isns_object_list_remove(&scope->ic_objects, obj);
++}
++
++/*
++ * Get all objects related through a portal group, optionally
++ * including the portal group objects themselves
++ */
++static void
++__isns_scope_get_pg_related(isns_scope_t *scope,
++		const isns_object_t *obj,
++		isns_object_list_t *result)
++{
++	isns_object_list_t temp = ISNS_OBJECT_LIST_INIT;
++	unsigned int	i;
++
++	/* Get all portal groups related to this object */
++	isns_db_get_relationship_objects(scope->ic_db,
++			obj, ISNS_RELATION_PORTAL_GROUP, &temp);
++
++	/* Include all portals/nodes that we can reach. */
++	for (i = 0; i < temp.iol_count; ++i) {
++		isns_object_t	*pg, *other;
++		uint32_t	pgt;
++
++		pg = temp.iol_data[i];
++
++		/* Skip any portal group objects with a PG tag of 0;
++		 * these actually deny access. */
++		if (!isns_object_get_uint32(pg, ISNS_TAG_PG_TAG, &pgt)
++		 || pgt == 0)
++			continue;
++
++		/* Get the other object.
++		 * Note that isns_relation_get_other doesn't
++		 * bump the reference count, so there's no need
++		 * to call isns_object_release(other). */
++		other = isns_relation_get_other(pg->ie_relation, obj);
++		if (other->ie_state != ISNS_OBJECT_STATE_MATURE)
++			continue;
++
++		isns_object_list_append(result, other);
++		isns_object_list_append(result, pg);
++	}
++
++	isns_object_list_destroy(&temp);
++}
++
++/*
++ * Get all portals related to the given node.
++ *
++ * 2.2.2
++ * Placing Portals of a Network Entity into Discovery Domains allows
++ * administrators to indicate the preferred IP Portal interface through
++ * which storage traffic should access specific Storage Nodes of that
++ * Network Entity.  If no Portals of a Network Entity have been placed
++ * into a DD, then queries scoped to that DD SHALL report all Portals of
++ * that Network Entity.  If one or more Portals of a Network Entity have
++ * been placed into a DD, then queries scoped to that DD SHALL report
++ * only those Portals that have been explicitly placed in the DD.
++ */
++static void
++__isns_scope_get_portals(isns_scope_t *scope,
++		const isns_object_t *node,
++		isns_object_list_t *portals,
++		isns_object_list_t *pgs,
++		int unique)
++{
++	isns_object_list_t related = ISNS_OBJECT_LIST_INIT;
++	unsigned int	i, specific = 0;
++
++	/* Get all portals and portal groups related to the
++	 * given node. This will put pairs of (portal, portal-group)
++	 * on the list.
++	 */
++	__isns_scope_get_pg_related(scope, node, &related);
++
++	/* If we're querying for our own portals, don't limit
++	 * visibility. */
++	if (node == scope->ic_source_node)
++		goto report_all_portals;
++
++	/* Check if any of the portals is mentioned in the DD
++	 * FIXME: There is some ambiguity over what the right
++	 * answer is when you have two nodes (initiator, target),
++	 * and two discovery domains linking the two. One
++	 * DD mentions a specific portal through which target
++	 * should be accessed; the other DD does not (allowing
++	 * use of any portal in that entity). Which portals
++	 * to return here?
++	 * We go for the strict interpretation, ie if *any* DD
++	 * restricts access to certain portals, we report only
++	 * those.
++	 */
++	for (i = 0; i < related.iol_count; i += 2) {
++		isns_object_t *portal = related.iol_data[i];
++
++		if (isns_object_list_contains(&scope->ic_dd_portals, portal)) {
++			if (portals
++			 && !(unique || isns_object_list_contains(portals, portal)))
++				isns_object_list_append(portals, portal);
++			if (pgs)
++				isns_object_list_append(pgs,
++						related.iol_data[i + 1]);
++			specific++;
++		}
++	}
++
++	if (specific)
++		goto out;
++
++report_all_portals:
++	/* No specific portal given for this node. Add them all. */
++	for (i = 0; i < related.iol_count; i += 2) {
++		isns_object_t *portal = related.iol_data[i];
++
++		if (portals
++		 && !(unique && isns_object_list_contains(portals, portal)))
++			isns_object_list_append(portals, portal);
++		if (pgs)
++			isns_object_list_append(pgs,
++					related.iol_data[i + 1]);
++	}
++
++out:
++	isns_object_list_destroy(&related);
++}
++
++/*
++ * Get all nodes reachable through a given portal
++ * This is really the same as __isns_scope_get_portals
++ * minus the special casing for preferred portals.
++ * Still, let's put this into it's own function - the whole
++ * thing is already complex enough already.
++ */
++static void
++__isns_scope_get_nodes(isns_scope_t *scope,
++		const isns_object_t *portal,
++		isns_object_list_t *nodes,
++		isns_object_list_t *pgs,
++		int unique)
++{
++	isns_object_list_t related = ISNS_OBJECT_LIST_INIT;
++	unsigned int	i;
++
++	/* Get all nodes and portal groups related to the
++	 * given node. This will put pairs of (nodes, portal-group)
++	 * on the list.
++	 */
++	__isns_scope_get_pg_related(scope, portal, &related);
++
++	for (i = 0; i < related.iol_count; i += 2) {
++		isns_object_t *node = related.iol_data[i];
++
++		if (nodes
++		 && !(unique && isns_object_list_contains(nodes, node)))
++			isns_object_list_append(nodes, node);
++		if (pgs)
++			isns_object_list_append(pgs,
++					related.iol_data[i + 1]);
++	}
++
++	isns_object_list_destroy(&related);
++}
++
++static void
++__isns_scope_get_default_dd(isns_scope_t *scope)
++{
++	isns_object_t	*obj;
++
++	if (isns_config.ic_use_default_domain) {
++		obj = isns_create_default_domain();
++		isns_object_list_append(&scope->ic_objects, obj);
++		isns_object_release(obj);
++	}
++}
++
++
++/*
++ * Scope the query
++ */
++static void
++__isns_scope_prepare_query(isns_scope_t *scope,
++		isns_object_template_t *tmpl)
++{
++	isns_object_list_t *nodes;
++	unsigned int	i;
++
++	/* Global and default scope have no source node; they're just
++	 * a list of objects.
++	 */
++	if (scope->ic_source_node == NULL)
++		return;
++
++	if (scope->ic_query_class) {
++		if (scope->ic_query_class == tmpl)
++			return;
++		isns_object_list_destroy(&scope->ic_objects);
++	}
++	scope->ic_query_class = tmpl;
++
++	nodes = &scope->ic_dd_nodes;
++	if (tmpl == &isns_entity_template) {
++		for (i = 0; i < nodes->iol_count; ++i) {
++			isns_object_t *obj = nodes->iol_data[i];
++
++			if (obj->ie_container)
++				isns_object_list_append(&scope->ic_objects,
++						obj->ie_container);
++		}
++	} else
++	if (tmpl == &isns_iscsi_node_template) {
++		for (i = 0; i < nodes->iol_count; ++i) {
++			isns_object_t *obj = nodes->iol_data[i];
++
++			isns_object_list_append(&scope->ic_objects, obj);
++		}
++	} else
++	if (tmpl == &isns_portal_template) {
++		for (i = 0; i < nodes->iol_count; ++i) {
++			isns_object_t *obj = nodes->iol_data[i];
++
++			__isns_scope_get_portals(scope, obj,
++					&scope->ic_objects, NULL, 0);
++		}
++	} else
++	if (tmpl == &isns_iscsi_pg_template) {
++		for (i = 0; i < nodes->iol_count; ++i) {
++			isns_object_t *obj = nodes->iol_data[i];
++
++			__isns_scope_get_portals(scope, obj,
++					NULL, &scope->ic_objects, 0);
++		}
++	} else
++	if (tmpl == &isns_dd_template) {
++		isns_object_t	*node = scope->ic_source_node;
++
++		if (node && !isns_bitvector_is_empty(node->ie_membership))
++			isns_bitvector_foreach(node->ie_membership,
++					__isns_scope_collect_dd,
++					scope);
++		else
++			__isns_scope_get_default_dd(scope);
++	}
++
++	isns_object_list_uniq(&scope->ic_objects);
++}
++
++static int
++__isns_scope_collect_dd(uint32_t dd_id, void *ptr)
++{
++	isns_scope_t *scope = ptr;
++	isns_object_t *dd;
++
++	dd = isns_db_vlookup(scope->ic_db, &isns_dd_template,
++			ISNS_TAG_DD_ID, dd_id,
++			0);
++	if (dd) {
++		isns_object_list_append(&scope->ic_objects, dd);
++		isns_object_release(dd);
++	}
++
++	return 0;
++}
++
++/*
++ * Lookup functions for scope
++ */
++int
++isns_scope_gang_lookup(isns_scope_t *scope,
++				isns_object_template_t *tmpl,
++				const isns_attr_list_t *match,
++				isns_object_list_t *result)
++{
++	isns_assert(tmpl);
++
++	if (!scope)
++		return 0;
++
++	__isns_scope_prepare_query(scope, tmpl);
++	return isns_object_list_gang_lookup(&scope->ic_objects,
++			tmpl, match, result);
++}
++
++/*
++ * Get related objects.
++ * This is used by the query code.
++ */
++void
++isns_scope_get_related(isns_scope_t *scope,
++				const isns_object_t *origin,
++				unsigned int type_mask,
++				isns_object_list_t *result)
++{
++	isns_object_template_t *tmpl = origin->ie_template;
++	isns_object_list_t	nodes_result = ISNS_OBJECT_LIST_INIT;
++	isns_object_list_t	portals_result = ISNS_OBJECT_LIST_INIT;
++	isns_object_list_t	*members = &scope->ic_dd_nodes;
++	unsigned int		i;
++
++	if (tmpl == &isns_entity_template) {
++		/* Entity: include all storage nodes contained,
++		 * the portals through which to reach them, and
++		 * the portal groups for those. */
++		for (i = 0; i < members->iol_count; ++i) {
++			isns_object_t *obj = members->iol_data[i];
++
++			if (obj->ie_container != origin)
++				continue;
++
++			isns_object_list_append(&nodes_result, obj);
++			__isns_scope_get_portals(scope, obj,
++						&portals_result,
++						&portals_result, 1);
++		}
++	} else
++	if (tmpl == &isns_iscsi_node_template) {
++		/* Storage node: include all portals through
++		 * which it can be reached, and the portal
++		 * groups for those. */
++		__isns_scope_get_portals(scope, origin,
++					&portals_result,
++					&portals_result, 1);
++		/* FIXME: Include all discovery domains the
++		 * node is a member of. */
++	} else
++	if (tmpl == &isns_portal_template) {
++		/* Portal: include all storage nodes which can
++		 * be reached through it, and the portal groups
++		 * for those. */
++		__isns_scope_get_nodes(scope, origin,
++					&portals_result,
++					&portals_result, 1);
++	} else
++	if (tmpl == &isns_iscsi_pg_template) {
++		/* Portal group: PGs *are* a relationship, but
++		 * unclear how this should be handled.
++		 * Return nothing for now. */
++	} else
++	if (tmpl == &isns_dd_template) {
++		/* Discovery domain: no related objects. */
++	}
++
++	isns_object_list_append_list(result, &nodes_result);
++	isns_object_list_append_list(result, &portals_result);
++
++	isns_object_list_destroy(&nodes_result);
++	isns_object_list_destroy(&portals_result);
++}
++
++isns_object_t *
++isns_scope_get_next(isns_scope_t *scope,
++				isns_object_template_t *tmpl,
++				const isns_attr_list_t *current,
++				const isns_attr_list_t *match)
++{
++	if (!tmpl || !scope)
++		return NULL;
++
++	__isns_scope_prepare_query(scope, tmpl);
++	return __isns_db_get_next(&scope->ic_objects, tmpl, current, match);
++}
+diff --git a/utils/open-isns/security.c b/utils/open-isns/security.c
+new file mode 100644
+index 0000000..548ce18
+--- /dev/null
++++ b/utils/open-isns/security.c
+@@ -0,0 +1,437 @@
++/*
++ * Security functions for iSNS
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include "isns.h"
++#include "security.h"
++#include "source.h"
++#include "util.h"
++#include "config.h"
++
++#ifdef WITH_SECURITY
++
++/*
++ * Allocate a security peer
++ */
++static isns_principal_t *
++isns_create_principal(const char *spi, size_t spi_len, EVP_PKEY *pk)
++{
++	char		keydesc[32];
++	isns_principal_t *peer;
++
++	peer = isns_calloc(1, sizeof(*peer));
++	peer->is_users = 1;
++	if (spi) {
++		peer->is_name = isns_malloc(spi_len + 1);
++		memcpy(peer->is_name, spi, spi_len);
++		peer->is_name[spi_len] = '\0';
++		peer->is_namelen = spi_len;
++	}
++
++	peer->is_key = pk;
++	if (pk) {
++		const char	*algo;
++
++		switch (pk->type) {
++		case EVP_PKEY_DSA: algo = "DSA"; break;
++		case EVP_PKEY_RSA: algo = "RSA"; break;
++		default: algo = "unknown"; break;
++		}
++
++		snprintf(keydesc, sizeof(keydesc), " (%s/%u)",
++				algo, EVP_PKEY_bits(pk));
++	}
++
++	isns_debug_auth("Created security principal \"%s\"%s\n",
++			peer->is_name, keydesc);
++	return peer;
++}
++
++static void
++isns_principal_set_key(isns_principal_t *princ, EVP_PKEY *key)
++{
++	if (princ->is_key == key)
++		return;
++	if (princ->is_key)
++		EVP_PKEY_free(princ->is_key);
++	princ->is_key = key;
++}
++
++void
++isns_principal_free(isns_principal_t *peer)
++{
++	if (!peer)
++		return;
++
++	isns_assert(peer->is_users);
++	if (--(peer->is_users))
++		return;
++
++	if (peer->is_name)
++		isns_free(peer->is_name);
++	if (peer->is_key)
++		EVP_PKEY_free(peer->is_key);
++	isns_policy_release(peer->is_policy);
++	isns_free(peer);
++}
++
++/*
++ * Set the principal's name
++ */
++void
++isns_principal_set_name(isns_principal_t *princ, const char *spi)
++{
++	isns_assign_string(&princ->is_name, spi);
++	isns_debug_auth("Setting principal name to \"%s\"\n", spi);
++}
++
++const char *
++isns_principal_name(const isns_principal_t *princ)
++{
++	return princ->is_name;
++}
++
++/*
++ * Cache policy in the principal object.
++ */
++void
++isns_principal_set_policy(isns_principal_t *princ,
++		isns_policy_t *policy)
++{
++	if (policy)
++		policy->ip_users++;
++	isns_policy_release(princ->is_policy);
++	princ->is_policy = policy;
++}
++
++/*
++ * Key management functions for a security context.
++ */
++isns_principal_t *
++isns_security_load_privkey(isns_security_t *ctx, const char *filename)
++{
++	EVP_PKEY	*pkey;
++
++	isns_debug_auth("Loading private %s key from %s\n",
++				ctx->is_name, filename);
++	if (!ctx->is_load_private)
++		return NULL;
++	if (!(pkey = ctx->is_load_private(ctx, filename))) {
++		isns_error("Unable to load private %s key from %s\n",
++				ctx->is_name, filename);
++		return NULL;
++	}
++
++	return isns_create_principal(NULL, 0, pkey);
++}
++
++isns_principal_t *
++isns_security_load_pubkey(isns_security_t *ctx, const char *filename)
++{
++	EVP_PKEY	*pkey;
++
++	isns_debug_auth("Loading public %s key from %s\n",
++				ctx->is_name, filename);
++	if (!ctx->is_load_public)
++		return NULL;
++	if (!(pkey = ctx->is_load_public(ctx, filename))) {
++		isns_error("Unable to load public %s key from %s\n",
++				ctx->is_name, filename);
++		return NULL;
++	}
++
++	return isns_create_principal(NULL, 0, pkey);
++}
++
++void
++isns_security_set_identity(isns_security_t *ctx, isns_principal_t *princ)
++{
++	if (princ)
++		princ->is_users++;
++	if (ctx->is_self)
++		isns_principal_free(ctx->is_self);
++	ctx->is_self = princ;
++}
++
++void
++isns_add_principal(isns_security_t *ctx, isns_principal_t *princ)
++{
++	if (princ)
++		princ->is_users++;
++	princ->is_next = ctx->is_peers;
++	ctx->is_peers = princ;
++}
++
++isns_principal_t *
++isns_get_principal(isns_security_t *ctx, const char *spi, size_t spi_len)
++{
++	isns_principal_t *princ;
++	isns_policy_t	*policy;
++	isns_keystore_t *ks;
++	EVP_PKEY	*pk;
++
++	ks = ctx->is_peer_keys;
++
++	for (princ = ctx->is_peers; princ; princ = princ->is_next) {
++		/* In a client socket, we set the (expected)
++		 * public key of the peer through
++		 * isns_security_set_peer_key, which will
++		 * just put it on the peers list.
++		 * This key usually has no name.
++		 */
++		if (princ->is_name == NULL) {
++			princ->is_users++;
++			return princ;
++		}
++		if (spi_len == princ->is_namelen
++		 && !memcmp(princ->is_name, spi, spi_len)) {
++			/* Check whether the cached key and policy
++			 * might be stale. */
++			if (ks && ks->ic_generation != princ->is_generation) {
++				pk = ks->ic_find(ks, spi, spi_len);
++				if (pk == NULL) {
++					isns_debug_auth("Unable to refresh key "
++						"for principal %.*s - probably deleted\n",
++						spi_len, spi);
++					return NULL;
++				}
++				isns_debug_auth("Refresh key for principal %.*s\n",
++						spi_len, spi);
++				isns_principal_set_key(princ, pk);
++				princ->is_users++;
++				goto refresh_policy;
++			}
++			princ->is_users++;
++			return princ;
++		}
++	}
++
++	if ((ks = ctx->is_peer_keys) == NULL)
++		return NULL;
++
++	if (!(pk = ks->ic_find(ks, spi, spi_len)))
++		return NULL;
++	princ = isns_create_principal(spi, spi_len, pk);
++
++	/* Add it to the list */
++	princ->is_next = ctx->is_peers;
++	ctx->is_peers = princ;
++	princ->is_users++;
++
++	/* Bind the policy for this peer */
++refresh_policy:
++	if (!ks->ic_get_policy
++	 || !(policy = ks->ic_get_policy(ks, spi, spi_len)))
++		policy = isns_policy_default(spi, spi_len);
++
++	/* If no entity is set, use the SPI */
++	if (policy->ip_entity == NULL)
++		isns_assign_string(&policy->ip_entity, policy->ip_name);
++
++	/* If the list of permitted node names is empty,
++	 * default to the standard pattern derived from
++	 * the reversed entity name */
++	if (policy->ip_node_names.count == 0) {
++		char	*pattern;
++
++		pattern = isns_build_source_pattern(policy->ip_entity);
++		if (pattern != NULL)
++			isns_string_array_append(&policy->ip_node_names,
++					pattern);
++		isns_free(pattern);
++	}
++
++	isns_principal_set_policy(princ, policy);
++	isns_policy_release(policy);
++
++	/* Remember the keystore generation number */
++	princ->is_generation = ks->ic_generation;
++
++	return princ;
++}
++
++/*
++ * Create a keystore for a security context.
++ * Key stores let the server side retrieve the
++ * keys associated with a given SPI.
++ *
++ * For now, we support just simple key stores,
++ * but this could be extended to support
++ * URLs such as ldaps://ldap.example.com
++ */
++isns_keystore_t *
++isns_create_keystore(const char *spec)
++{
++	if (*spec != '/')
++		return NULL;
++
++	return isns_create_simple_keystore(spec);
++}
++
++/*
++ * Attach the keystore to the security context
++ */
++void
++isns_security_set_keystore(isns_security_t *ctx,
++			isns_keystore_t *ks)
++{
++	ctx->is_peer_keys = ks;
++}
++
++/*
++ * Check that the client supplied time stamp is within a
++ * certain window.
++ */
++static int
++isns_security_check_timestamp(isns_security_t *ctx,
++					isns_principal_t *peer,
++					uint64_t timestamp)
++{
++	int64_t	delta;
++
++	/* The time stamp must not be earlier than timestamp_jitter
++	 * before the last message received. */
++	if (peer->is_timestamp) {
++		delta = timestamp - peer->is_timestamp;
++		if (delta < -(int64_t) ctx->is_timestamp_jitter)
++			return 0;
++	}
++
++	/* We allow the client's clock to diverge from ours, within
++	 * certain limits. */
++	if (ctx->is_replay_window != 0) {
++		time_t	now = time(NULL);
++
++		delta = timestamp - now;
++		if (delta < 0)
++			delta = -delta;
++		if (delta > ctx->is_replay_window)
++			return 0;
++	}
++
++	peer->is_timestamp = timestamp;
++	return 1;
++}
++
++int
++isns_security_sign(isns_security_t *ctx, isns_principal_t *peer,
++		buf_t *bp, struct isns_authblk *auth)
++{
++	if (!ctx->is_sign) {
++		isns_debug_auth("isns_security_sign: auth context without "
++				"sign handler.\n");
++		return 0;
++	}
++	if (!ctx->is_sign(ctx, peer, bp, auth)) {
++		isns_debug_auth("Failed to sign message, spi=%s\n",
++				peer->is_name);
++		return 0;
++	}
++
++	return 1;
++}
++
++int
++isns_security_verify(isns_security_t *ctx, isns_principal_t *peer,
++		buf_t *bp, struct isns_authblk *auth)
++{
++	if (!isns_security_check_timestamp(ctx, peer, auth->iab_timestamp)) {
++		isns_debug_auth("Possible replay attack (bad timestamp) "
++				"from spi=%s\n", peer->is_name);
++		return 0;
++	}
++
++	if (!ctx->is_verify) {
++		isns_debug_auth("isns_security_verify: auth context without "
++				"verify handler.\n");
++		return 0;
++	}
++	if (!ctx->is_verify(ctx, peer, bp, auth)) {
++		isns_debug_auth("Failed to authenticate message, spi=%s\n",
++				peer->is_name);
++		return 0;
++	}
++
++	return 1;
++}
++
++/*
++ * Initialize security services.
++ */
++int
++isns_security_init(void)
++{
++	if (!isns_config.ic_dsa.param_file) {
++		isns_error("No DSA parameter file - please edit configuration\n");
++		return 0;
++	}
++
++	if (!isns_dsa_init_params(isns_config.ic_dsa.param_file))
++		return 0;
++
++	if (!isns_config.ic_auth_key_file) {
++		isns_error("No AuthKey specified; please edit configuration\n");
++		return 0;
++	}
++
++	if (!isns_dsa_init_key(isns_config.ic_auth_key_file))
++		return 0;
++
++	return 1;
++}
++
++#else /* WITH_SECURITY */
++
++static void
++isns_no_security(void)
++{
++	static int complain = 0;
++
++	if (complain++ < 5)
++		isns_error("iSNS authentication disabled in this build\n");
++}
++
++int
++isns_security_init(void)
++{
++	isns_no_security();
++	return 0;
++}
++
++isns_keystore_t *
++isns_create_keystore(const char *spec)
++{
++	isns_no_security();
++	return NULL;
++}
++
++void
++isns_security_set_keystore(isns_security_t *ctx,
++			isns_keystore_t *ks)
++{
++	isns_no_security();
++}
++
++void
++isns_principal_free(isns_principal_t *peer)
++{
++}
++
++isns_principal_t *
++isns_get_principal(isns_security_t *ctx, const char *spi, size_t spi_len)
++{
++	return NULL;
++}
++
++const char *
++isns_principal_name(const isns_principal_t *princ)
++{
++	return NULL;
++}
++
++#endif /* WITH_SECURITY */
+diff --git a/utils/open-isns/security.h b/utils/open-isns/security.h
+new file mode 100644
+index 0000000..4b928ff
+--- /dev/null
++++ b/utils/open-isns/security.h
+@@ -0,0 +1,185 @@
++/*
++ * Security functions for iSNS
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_SECURITY_H
++#define ISNS_SECURITY_H
++#include "buffer.h"
++#include "util.h"
++
++
++#ifdef WITH_SECURITY
++#include <openssl/evp.h>
++#else
++#define EVP_PKEY void
++#endif
++
++/*
++ * Security context
++ */
++struct isns_security {
++	const char *		is_name;
++	unsigned int		is_type;
++	unsigned int		is_replay_window;
++	unsigned int		is_timestamp_jitter;
++
++	/* Our own key and identity */
++	isns_principal_t *	is_self;
++
++	/* Key store for peer keys */
++	isns_principal_t *	is_peers;
++	isns_keystore_t *	is_peer_keys;
++
++	EVP_PKEY *		(*is_load_private)(isns_security_t *ctx,
++					const char *filename);
++	EVP_PKEY *		(*is_load_public)(isns_security_t *ctx,
++					const char *filename);
++	int			(*is_verify)(isns_security_t *ctx,
++					isns_principal_t *peer,
++					buf_t *pdu,
++					const struct isns_authblk *);
++	int			(*is_sign)(isns_security_t *ctx,
++					isns_principal_t *peer,
++					buf_t *pdu,
++					struct isns_authblk *);
++};
++
++struct isns_principal {
++	unsigned int		is_users;
++	isns_principal_t *	is_next;
++	char *			is_name;
++	unsigned int		is_namelen;
++	EVP_PKEY *		is_key;
++	unsigned int		is_generation;
++	uint64_t		is_timestamp;
++
++	isns_policy_t *		is_policy;
++};
++
++struct isns_policy {
++	unsigned int		ip_users;
++	unsigned int		ip_gen;
++
++	/* SPI */
++	char *			ip_name;
++
++	/* The client's entity name. This is usually
++	 * the FQDN. */
++	char *			ip_entity;
++
++	/* Bitmap of functions the client is
++	 * permitted to call. */
++	unsigned int		ip_functions;
++
++	/* Bitmap of object types the client is
++	 * permitted to register (uses iot_handle) */
++	unsigned int		ip_object_types;
++
++	/* Names of storage nodes the client is permitted
++	 * to register. */
++	struct string_array	ip_node_names;
++
++	/* Storage node types the client is permitted
++	 * to read or modify. */
++	unsigned int		ip_node_types;
++
++	/* The client's default Discovery Domain */
++	char *			ip_dd_default;
++};
++
++#define ISNS_PERMISSION_READ	0x01
++#define ISNS_PERMISSION_WRITE	0x02
++#define ISNS_ACCESS(t, p)	((p) << (2 * (t)))
++#define ISNS_ACCESS_W(t)	ISNS_ACCESS(t, ISNS_PERMISSION_WRITE)
++#define ISNS_ACCESS_R(t)	ISNS_ACCESS(t, ISNS_PERMISSION_READ)
++#define ISNS_ACCESS_RW(t)	ISNS_ACCESS(t, ISNS_PERMISSION_READ|ISNS_PERMISSION_WRITE)
++
++#define ISNS_DEFAULT_OBJECT_ACCESS \
++		ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_ENTITY) | \
++		ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_NODE) | \
++		ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_FC_PORT) | \
++		ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_FC_NODE) | \
++		ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_PORTAL) | \
++		ISNS_ACCESS_RW(ISNS_OBJECT_TYPE_PG) | \
++		ISNS_ACCESS_R(ISNS_OBJECT_TYPE_DD)
++
++struct isns_keystore {
++	char *			ic_name;
++	unsigned int		ic_generation;
++	EVP_PKEY *		(*ic_find)(isns_keystore_t *,
++					const char *, size_t);
++	isns_policy_t *		(*ic_get_policy)(isns_keystore_t *,
++					const char *, size_t);
++};
++
++extern isns_principal_t *	isns_get_principal(isns_security_t *,
++					const char *, size_t);
++extern int			isns_security_sign(isns_security_t *,
++					isns_principal_t *, buf_t *,
++					struct isns_authblk *);
++extern int			isns_security_verify(isns_security_t *,
++					isns_principal_t *, buf_t *,
++					struct isns_authblk *);
++extern int			isns_security_protected_entity(isns_security_t *,
++					const char *);
++
++extern isns_keystore_t *	isns_create_keystore(const char *);
++extern isns_keystore_t *	isns_create_simple_keystore(const char *);
++extern isns_keystore_t *	isns_create_db_keystore(isns_db_t *);
++
++extern int			isns_authblock_encode(buf_t *,
++					const struct isns_authblk *);
++extern int			isns_authblock_decode(buf_t *,
++					struct isns_authblk *);
++
++extern isns_policy_t *		__isns_policy_alloc(const char *, size_t);
++extern isns_policy_t *		isns_policy_bind(const isns_message_t *);
++extern void			isns_principal_set_policy(isns_principal_t *,
++					isns_policy_t *);
++extern void			isns_policy_release(isns_policy_t *);
++extern int			isns_policy_validate_function(const isns_policy_t *,
++					const isns_message_t *);
++extern int			isns_policy_validate_source(const isns_policy_t *,
++					const isns_source_t *);
++extern int			isns_policy_validate_object_access(const isns_policy_t *,
++					const isns_source_t *,
++					const isns_object_t *,
++					unsigned int);
++extern int			isns_policy_validate_object_update(const isns_policy_t *,
++					const isns_source_t *,
++					const isns_object_t *,
++					const isns_attr_list_t *,
++					unsigned int);
++extern int			isns_policy_validate_object_creation(const isns_policy_t *,
++					const isns_source_t *,
++					isns_object_template_t *,
++					const isns_attr_list_t *,
++					const isns_attr_list_t *,
++					unsigned int);
++extern int			isns_policy_validate_object_type(const isns_policy_t *,
++					isns_object_template_t *,
++					unsigned int function);
++extern int			isns_policy_validate_node_type(const isns_policy_t *,
++					uint32_t type);
++extern int			isns_policy_validate_entity(const isns_policy_t *,
++					const char *);
++extern int			isns_policy_validate_node_name(const isns_policy_t *,
++					const char *);
++extern int			isns_policy_validate_scn_bitmap(const isns_policy_t *,
++					uint32_t);
++extern const char *		isns_policy_default_entity(const isns_policy_t *);
++extern isns_policy_t *		isns_policy_default(const char *, size_t);
++extern isns_policy_t *		isns_policy_server(void);
++
++extern EVP_PKEY *		isns_dsa_decode_public(const void *, size_t);
++extern int			isns_dsa_encode_public(EVP_PKEY *,
++					void **, size_t *);
++extern EVP_PKEY *		isns_dsa_load_public(const char *);
++extern int			isns_dsa_store_private(const char *, EVP_PKEY *);
++extern EVP_PKEY *		isns_dsa_generate_key(void);
++extern int			isns_dsa_init_params(const char *);
++extern int			isns_dsa_init_key(const char *);
++
++#endif /* ISNS_SECURITY_H */
+diff --git a/utils/open-isns/server.c b/utils/open-isns/server.c
+new file mode 100644
+index 0000000..0f1c937
+--- /dev/null
++++ b/utils/open-isns/server.c
+@@ -0,0 +1,236 @@
++/*
++ * iSNS server side functions
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include "isns.h"
++#include "util.h"
++#include "security.h"
++#include "message.h"
++
++static int	isns_not_supported(isns_server_t *, isns_simple_t *, isns_simple_t **);
++
++struct isns_service_ops		isns_default_service_ops = {
++	.process_registration	  = isns_process_registration,
++	.process_query		  = isns_process_query,
++	.process_getnext	  = isns_process_getnext,
++	.process_deregistration	  = isns_process_deregistration,
++	.process_scn_registration = isns_process_scn_register,
++	.process_scn_deregistration = isns_process_scn_deregistration,
++	.process_scn_event	  = isns_not_supported,
++	.process_dd_registration  = isns_process_dd_registration,
++	.process_dd_deregistration= isns_process_dd_deregistration,
++};
++
++struct isns_service_ops		isns_callback_service_ops = {
++	.process_esi		  = isns_process_esi,
++	.process_scn		  = isns_process_scn,
++};
++
++/*
++ * Create a server object
++ */
++isns_server_t *
++isns_create_server(isns_source_t *source, isns_db_t *db,
++			struct isns_service_ops *ops)
++{
++	isns_server_t	*srv;
++
++	if (source == NULL) {
++		isns_error("%s: source name not set\n", __FUNCTION__);
++		return NULL;
++	}
++
++	srv = isns_calloc(1, sizeof(*srv));
++	srv->is_source = isns_source_get(source);
++	srv->is_db = db;
++	srv->is_ops = ops;
++
++	return srv;
++}
++
++void
++isns_server_set_scn_callback(isns_server_t *srv, isns_scn_callback_fn_t *func)
++{
++	srv->is_scn_callback = func;
++}
++
++/*
++ * Try to handle transactions safely.
++ * This isn't perfect, because there's state outside the DB (for instance
++ * the DD information)
++ */
++static int
++isns_begin_write_operation(isns_server_t *srv, isns_simple_t *msg, int *status)
++{
++	isns_db_begin_transaction(srv->is_db);
++	return 1;
++}
++
++static void
++isns_end_write_operation(isns_server_t *srv, isns_simple_t *msg, int *status)
++{
++	if (*status == ISNS_SUCCESS)
++		isns_db_commit(srv->is_db);
++	else
++		isns_db_rollback(srv->is_db);
++}
++
++static inline int
++isns_begin_read_operation(isns_server_t *srv, isns_simple_t *msg, int *status)
++{
++	return 1;
++}
++
++static void
++isns_end_read_operation(isns_server_t *srv, isns_simple_t *msg, int *status)
++{
++}
++
++/*
++ * Process an incoming message
++ */
++isns_message_t *
++isns_process_message(isns_server_t *srv, isns_message_t *msg)
++{
++	struct isns_service_ops *ops = srv->is_ops;
++	uint16_t	function = msg->im_header.i_function;
++	int		status = ISNS_SUCCESS;
++	isns_simple_t	*call = NULL, *reply = NULL;
++	isns_message_t	*res_msg = NULL;
++	isns_db_t	*db = srv->is_db;
++
++	status = isns_simple_decode(msg, &call);
++	if (status) {
++		isns_debug_message("Failed to decode %s request: %s\n",
++				isns_function_name(msg->im_header.i_function),
++				isns_strerror(status));
++		goto reply;
++	}
++
++	isns_simple_print(call, isns_debug_message);
++
++	/* Set policy and privileges based on the
++	 * sender's identity. */
++	if (!(call->is_policy = isns_policy_bind(msg))) 
++		goto err_unauthorized;
++
++	if (!isns_policy_validate_function(call->is_policy, msg))
++		goto err_unauthorized;
++
++	/* Checks related to the message source.
++	 * Note - some messages do not use a source.
++	 */
++	if (call->is_source) {
++		/* Validate the message source. This checks whether the client
++		 * is permitted to use this source node name.
++		 * Beware - not all messages include a source.
++		 */
++		if (!isns_policy_validate_source(call->is_policy, call->is_source))
++			goto err_unauthorized;
++
++		/* This may fail if the source node isn't in the DB yet. */
++		isns_source_set_node(call->is_source, db);
++
++		/*
++		 * 6.2.6.  Registration Period
++		 *
++		 * The registration SHALL be removed from the iSNS database
++		 * if an iSNS Protocol message is not received from the
++		 * iSNS client before the registration period has expired.
++		 * Receipt of any iSNS Protocol message from the iSNS client
++		 * automatically refreshes the Entity Registration Period and
++		 * Entity Registration Timestamp.  To prevent a registration
++		 * from expiring, the iSNS client should send an iSNS Protocol
++		 * message to the iSNS server at intervals shorter than the
++		 * registration period.  Such a message can be as simple as a
++		 * query for one of its own attributes, using its associated
++		 * iSCSI Name or FC Port Name WWPN as the Source attribute.
++		 */
++		/* Thusly, we update the timestamps of all entities
++		 * registered by this source. */
++		isns_entity_touch(call->is_source->is_entity);
++	}
++
++	/* Handle the requested function. If the function vector is
++	 * NULL, silently discard the message. */
++	switch (function) {
++#define DO(rw, FUNCTION, __function) \
++	case FUNCTION:						\
++		if (!ops->__function)				\
++			goto no_reply;				\
++								\
++		if (!isns_begin_##rw##_operation(srv, call, &status)) \
++			break;					\
++		status = ops->__function(srv, call, &reply);	\
++		isns_end_##rw##_operation(srv, call, &status);	\
++		break
++
++	DO(write, ISNS_DEVICE_ATTRIBUTE_REGISTER, process_registration);
++	DO(read,  ISNS_DEVICE_ATTRIBUTE_QUERY, process_query);
++	DO(read,  ISNS_DEVICE_GET_NEXT, process_getnext);
++	DO(write, ISNS_DEVICE_DEREGISTER, process_deregistration);
++	DO(write, ISNS_DD_REGISTER, process_dd_registration);
++	DO(write, ISNS_DD_DEREGISTER, process_dd_deregistration);
++	DO(read,  ISNS_SCN_REGISTER, process_scn_registration);
++	DO(read,  ISNS_SCN_DEREGISTER, process_scn_deregistration);
++	DO(read,  ISNS_SCN_EVENT, process_scn_event);
++	DO(read,  ISNS_STATE_CHANGE_NOTIFICATION, process_scn);
++	DO(read,  ISNS_ENTITY_STATUS_INQUIRY, process_esi);
++	DO(read,  ISNS_HEARTBEAT, process_heartbeat);
++#undef DO
++
++	default:
++		isns_error("Function %s not supported\n",
++				isns_function_name(function));
++		status = ISNS_MESSAGE_NOT_SUPPORTED;
++		break;
++	}
++
++reply:
++	/* Commit any changes to the DB before we reply */
++	if (db)
++		isns_db_sync(db);
++
++	/* Send out SCN notifications */
++	isns_flush_events();
++
++	if (reply != NULL) {
++		reply->is_function |= 0x8000;
++		isns_simple_print(reply, isns_debug_message);
++
++		/* Encode the whole thing */
++		status = isns_simple_encode_response(reply, msg, &res_msg);
++	}
++
++	/* No reply, or error when encoding it:
++	 * just send the error, nothing else. */
++	if (res_msg == NULL) {
++		res_msg = isns_create_reply(msg);
++		if (status == ISNS_SUCCESS)
++			status = ISNS_INTERNAL_ERROR;
++	}
++
++	isns_debug_message("response status 0x%04x (%s)\n",
++			status, isns_strerror(status));
++
++	if (status != ISNS_SUCCESS)
++		isns_message_set_error(res_msg, status);
++
++no_reply:
++	isns_simple_free(call);
++	if (reply)
++		isns_simple_free(reply);
++	return res_msg;
++
++err_unauthorized:
++	status = ISNS_SOURCE_UNAUTHORIZED;
++	goto reply;
++}
++
++int
++isns_not_supported(isns_server_t *srv, isns_simple_t *call, isns_simple_t **replyp)
++{
++	return ISNS_MESSAGE_NOT_SUPPORTED;
++}
+diff --git a/utils/open-isns/simple.c b/utils/open-isns/simple.c
+new file mode 100644
+index 0000000..1af89fd
+--- /dev/null
++++ b/utils/open-isns/simple.c
+@@ -0,0 +1,727 @@
++/*
++ * Common handling for iSNS message parsing
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ *
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include "isns.h"
++#include "attrs.h"
++#include "message.h"
++#include "objects.h"
++#include "security.h"
++#include "socket.h"
++#include "util.h"
++
++typedef void isns_simple_callback_fn_t(uint32_t, int status, isns_simple_t *);
++
++static int	isns_attr_list_scanner_get_pg(struct isns_attr_list_scanner *st);
++
++/*
++ * Allocate an empty simple message
++ */
++static isns_simple_t *
++__isns_alloc_simple(void)
++{
++	isns_simple_t	*simp;
++
++	simp = isns_calloc(1, sizeof(*simp));
++
++	isns_attr_list_init(&simp->is_message_attrs);
++	isns_attr_list_init(&simp->is_operating_attrs);
++
++	return simp;
++}
++
++/*
++ * Create a simple message, and set the source name
++ */
++isns_simple_t *
++isns_simple_create(uint32_t function, isns_source_t *source,
++		const isns_attr_list_t *key)
++{
++	isns_simple_t	*simp;
++
++	simp = __isns_alloc_simple();
++	simp->is_function = function;
++	simp->is_source = source;
++	if (source != NULL)
++		source->is_users++;
++
++	if (key)
++		isns_attr_list_copy(&simp->is_message_attrs, key);
++
++	return simp;
++}
++
++/*
++ * Perform a call to the server, waiting for the response.
++ */
++int
++isns_simple_call(isns_socket_t *sock, isns_simple_t **inout)
++{
++	isns_simple_t	*simp = *inout;
++	isns_message_t	*msg, *resp;
++	int		status;
++
++	isns_simple_print(simp, isns_debug_message);
++
++	status = isns_simple_encode(simp, &msg);
++	if (status != ISNS_SUCCESS) {
++		isns_error("Unable to encode %s: %s\n",
++				isns_function_name(simp->is_function),
++				isns_strerror(status));
++		return status;
++	}
++
++	isns_debug_message("Sending request, len=%d\n",
++			buf_avail(msg->im_payload));
++
++	resp = isns_socket_call(sock, msg,
++			isns_config.ic_network.call_timeout);
++	isns_assert(msg->im_users == 1);
++	isns_message_release(msg);
++
++	if (resp == NULL) {
++		isns_error("Timed out while waiting for reply\n");
++		return ISNS_INTERNAL_ERROR;
++	}
++
++	isns_debug_message("Received reply, len=%d\n",
++			buf_avail(resp->im_payload));
++	isns_assert(resp->im_users == 1);
++
++	status = isns_message_status(resp);
++	if (status != ISNS_SUCCESS) {
++		isns_message_release(resp);
++		return status;
++	}
++
++	status = isns_simple_decode(resp, &simp);
++	isns_message_release(resp);
++
++	if (status) {
++		isns_error("Unable to decode server response: %s (status 0x%04x)\n",
++				isns_strerror(status), status);
++		return status;
++	}
++
++	isns_simple_print(simp, isns_debug_message);
++
++	isns_simple_free(*inout);
++	*inout = simp;
++	return ISNS_SUCCESS;
++}
++
++/*
++ * This callback is invoked from the network layer when
++ * we received a response to an async message
++ */
++static void
++isns_simple_recv_response(isns_message_t *cmsg, isns_message_t *rmsg)
++{
++	isns_simple_callback_fn_t *user_callback;
++	isns_simple_t	*resp = NULL;
++	int		status = ISNS_INTERNAL_ERROR;
++
++	/* rmsg being NULL means the call timed out. */
++	if (rmsg == NULL)
++		goto callback;
++
++	status = isns_message_status(rmsg);
++	if (status != ISNS_SUCCESS) {
++		isns_error("Server flags error: %s (status 0x%04x)\n",
++			    isns_strerror(status), status);
++		goto callback;
++	}
++
++	status = isns_simple_decode(rmsg, &resp);
++	if (status) {
++		isns_error("Unable to decode server response: %s (status 0x%04x)\n",
++				isns_strerror(status), status);
++		resp = NULL;
++		goto callback;
++	}
++
++	isns_simple_print(resp, isns_debug_message);
++
++callback:
++	user_callback = cmsg->im_calldata;
++	if (user_callback)
++		user_callback(cmsg->im_xid, status, resp);
++	if (resp)
++		isns_simple_free(resp);
++}
++
++/*
++ * Transmit a call, without waiting for the response.
++ */
++int
++isns_simple_transmit(isns_socket_t *sock, isns_simple_t *call,
++			const isns_portal_info_t *dest,
++			unsigned int timeout,
++			isns_simple_callback_fn_t *user_callback)
++{
++	isns_message_t	*msg;
++	int		status;
++
++	isns_simple_print(call, isns_debug_message);
++
++	status = isns_simple_encode(call, &msg);
++	if (status != ISNS_SUCCESS) {
++		isns_error("Unable to encode %s: %s\n",
++				isns_function_name(call->is_function),
++				isns_strerror(status));
++		return status;
++	}
++
++	isns_debug_message("Sending message, len=%d\n",
++			buf_avail(msg->im_payload));
++
++	if (user_callback) {
++		msg->im_callback = isns_simple_recv_response;
++		msg->im_calldata = user_callback;
++	}
++
++	if (!isns_socket_submit(sock, msg, timeout))
++		status = ISNS_INTERNAL_ERROR;
++	isns_message_release(msg);
++	return status;
++}
++
++/*
++ * Delete the simple message object
++ */
++void
++isns_simple_free(isns_simple_t *simp)
++{
++	if (simp == NULL)
++		return;
++
++	isns_attr_list_destroy(&simp->is_message_attrs);
++	isns_attr_list_destroy(&simp->is_operating_attrs);
++	isns_source_release(simp->is_source);
++	isns_policy_release(simp->is_policy);
++	isns_free(simp);
++}
++
++/*
++ * Get the source associated with this simple message
++ */
++isns_source_t *
++isns_simple_get_source(isns_simple_t *simp)
++{
++	return simp->is_source;
++}
++
++const isns_attr_list_t *
++isns_simple_get_attrs(isns_simple_t *simp)
++{
++	return &simp->is_operating_attrs;
++}
++
++/*
++ * Determine whether message includes a source attr.
++ */
++static inline int
++isns_simple_include_source(uint16_t function)
++{
++	if (function & 0x8000)
++		return 0;
++	switch (function) {
++	case ISNS_STATE_CHANGE_NOTIFICATION:
++	case ISNS_ENTITY_STATUS_INQUIRY:
++		return 0;
++	}
++	return 1;
++}
++
++/*
++ * Decode a simple message
++ */
++int
++isns_simple_decode(isns_message_t *msg, isns_simple_t **result)
++{
++	isns_simple_t	*simp = __isns_alloc_simple();
++	buf_t		*bp = msg->im_payload;
++	int		status = ISNS_SUCCESS;
++
++	simp->is_function = msg->im_header.i_function;
++	simp->is_xid = msg->im_xid;
++
++	if (isns_simple_include_source(simp->is_function)) {
++		status = isns_source_decode(bp, &simp->is_source);
++		if (status != ISNS_SUCCESS)
++			goto out;
++	}
++
++	switch (simp->is_function & 0x7FFF) {
++	case ISNS_ENTITY_STATUS_INQUIRY:
++	case ISNS_STATE_CHANGE_NOTIFICATION:
++		/* Server messages do not include a source */
++		status = isns_attr_list_decode(bp,
++				&simp->is_message_attrs);
++		break;
++
++	default:
++		status = isns_attr_list_decode_delimited(bp,
++				&simp->is_message_attrs);
++		if (status == ISNS_SUCCESS)
++			status = isns_attr_list_decode(bp,
++				&simp->is_operating_attrs);
++	}
++
++	if (msg->im_header.i_flags & ISNS_F_REPLACE)
++		simp->is_replace = 1;
++
++out:
++	if (status == ISNS_SUCCESS) {
++		*result = simp;
++	} else {
++		isns_simple_free(simp);
++		*result = NULL;
++	}
++	return status;
++}
++
++/*
++ * Encode a simple message reply or response
++ */
++static int
++__isns_simple_encode(isns_simple_t *simp, buf_t *bp)
++{
++	int	status = ISNS_SUCCESS;
++
++	if (isns_simple_include_source(simp->is_function)) {
++		if (simp->is_source == NULL) {
++			isns_error("Cannot encode %s message - caller forgot to set source\n",
++				  isns_function_name(simp->is_function));
++			return ISNS_SOURCE_UNKNOWN;
++		}
++		status = isns_source_encode(bp, simp->is_source);
++	}
++
++	if (status == ISNS_SUCCESS)
++		status = isns_attr_list_encode(bp, &simp->is_message_attrs);
++
++	/* Some functions have just one set of attrs. */
++	switch (simp->is_function & 0x7fff) {
++	/* It's not entirely clear which calls actually have the delimiter.
++	 * The spec is sometimes a little vague on this. */
++	case ISNS_SCN_DEREGISTER:
++	case ISNS_ENTITY_STATUS_INQUIRY:
++	case ISNS_STATE_CHANGE_NOTIFICATION:
++		break;
++
++	default:
++		if (status == ISNS_SUCCESS)
++			status = isns_encode_delimiter(bp);
++		if (status == ISNS_SUCCESS)
++			status = isns_attr_list_encode(bp, &simp->is_operating_attrs);
++		break;
++	}
++
++	return status;
++}
++
++int
++isns_simple_encode(isns_simple_t *simp, isns_message_t **result)
++{
++	isns_message_t *msg;
++	int status, flags;
++
++	flags = ISNS_F_CLIENT;
++	if (simp->is_replace)
++		flags |= ISNS_F_REPLACE;
++	msg = isns_create_message(simp->is_function, flags);
++
++	/* FIXME: for UDP sockets, isns_simple_t may contain a
++	   destination address. */
++
++	status = __isns_simple_encode(simp, msg->im_payload);
++	if (status != ISNS_SUCCESS) {
++		isns_message_release(msg);
++		msg = NULL;
++	}
++
++	/* Report the XID to the caller */
++	simp->is_xid = msg->im_xid;
++
++	*result = msg;
++	return status;
++}
++
++int
++isns_simple_encode_response(isns_simple_t *reg,
++		const isns_message_t *request, isns_message_t **result)
++{
++	isns_message_t *msg;
++	int status;
++
++	msg = isns_create_reply(request);
++
++	status = __isns_simple_encode(reg, msg->im_payload);
++	if (status != ISNS_SUCCESS) {
++		isns_message_release(msg);
++		msg = NULL;
++	}
++
++	*result = msg;
++	return status;
++}
++
++int
++isns_simple_decode_response(isns_message_t *resp, isns_simple_t **result)
++{
++	return isns_simple_decode(resp, result);
++}
++
++/*
++ * Extract the list of objects from a DevAttrReg/DevAttrQry
++ * response or similar.
++ */
++int
++isns_simple_response_get_objects(isns_simple_t *resp,
++		isns_object_list_t *result)
++{
++	struct isns_attr_list_scanner state;
++	int	status = ISNS_SUCCESS;
++
++	isns_attr_list_scanner_init(&state, NULL, &resp->is_operating_attrs);
++	while (1) {
++		isns_object_t	*obj;
++
++		status = isns_attr_list_scanner_next(&state);
++		if (status == ISNS_NO_SUCH_ENTRY) {
++			status = ISNS_SUCCESS;
++			break;
++		}
++		if (status)
++			break;
++
++		obj = isns_create_object(state.tmpl, &state.keys, NULL);
++
++		isns_object_set_attrlist(obj, &state.attrs);
++		if (obj != state.key_obj)
++			isns_object_list_append(result, obj);
++		isns_object_release(obj);
++	}
++
++	isns_attr_list_scanner_destroy(&state);
++	return status;
++}
++
++/*
++ * Print a simple message object
++ */
++void
++isns_simple_print(isns_simple_t *simp, isns_print_fn_t *fn)
++{
++	char	buffer[256];
++
++	if (fn == isns_debug_message
++	 && !isns_debug_enabled(DBG_MESSAGE))
++		return;
++
++	fn("---%s%s---\n",
++			isns_function_name(simp->is_function),
++			simp->is_replace? "[REPLACE]" : "");
++	if (simp->is_source) {
++		fn("Source:\n", buffer);
++		isns_attr_print(simp->is_source->is_attr, fn);
++	} else {
++		fn("Source: <empty>\n");
++	}
++
++	if (simp->is_message_attrs.ial_count == 0) {
++		fn("Message attributes: <empty list>\n");
++	} else {
++		fn("Message attributes:\n");
++		isns_attr_list_print(&simp->is_message_attrs, fn);
++	}
++	if (simp->is_operating_attrs.ial_count == 0) {
++		fn("Operating attributes: <empty list>\n");
++	} else {
++		fn("Operating attributes:\n");
++		isns_attr_list_print(&simp->is_operating_attrs, fn);
++	}
++}
++
++/*
++ * This set of functions analyzes the operating attrs of a registration,
++ * or a query response, and chops it up into separate chunks, one
++ * per objects.
++ *
++ * It always returns the keys and attrs for one object,
++ * following the ordering constraints laid out in the RFC.
++ */
++void
++isns_attr_list_scanner_init(struct isns_attr_list_scanner *st,
++			isns_object_t *key_obj,
++			const isns_attr_list_t *attrs)
++{
++	memset(st, 0, sizeof(*st));
++	st->orig_attrs = *attrs;
++	st->key_obj = key_obj;
++}
++
++void
++isns_attr_list_scanner_destroy(struct isns_attr_list_scanner *st)
++{
++	isns_attr_list_destroy(&st->keys);
++	isns_attr_list_destroy(&st->attrs);
++	memset(st, 0, sizeof(*st));
++}
++
++int
++isns_attr_list_scanner_next(struct isns_attr_list_scanner *st)
++{
++	isns_attr_t	*attr;
++	unsigned int	i, pos = st->pos;
++
++	isns_attr_list_destroy(&st->keys);
++	isns_attr_list_destroy(&st->attrs);
++
++	if (st->orig_attrs.ial_count <= pos)
++		return ISNS_NO_SUCH_ENTRY;
++
++	attr = st->orig_attrs.ial_data[pos];
++
++	/* handle those funky inlined PGT definitions */
++	if (st->pgt_next_attr && attr->ia_tag_id == st->pgt_next_attr)
++		return isns_attr_list_scanner_get_pg(st);
++
++	/* This isn't really structured programming anymore */
++	if (st->index_acceptable
++	 && (st->tmpl = isns_object_template_for_index_tag(attr->ia_tag_id)))
++		goto copy_attrs;
++
++	/*
++	 * Find the object template for the given key attr(s).
++	 * This function also enforces restrictions on the
++	 * order of key attributes.
++	 */
++	st->tmpl = isns_object_template_find(attr->ia_tag_id);
++	if (st->tmpl == NULL) {
++		isns_debug_protocol("%s: attr %u is not a key attr\n",
++				__FUNCTION__, attr->ia_tag_id);
++		return ISNS_INVALID_REGISTRATION;
++	}
++
++	/* Copy the key attrs */
++	for (i = 0; i < st->tmpl->iot_num_keys; ++i, ++pos) {
++		if (pos >= st->orig_attrs.ial_count) {
++			isns_debug_protocol("%s: incomplete %s object "
++					"(key attr %u missing)\n",
++					__FUNCTION__, st->tmpl->iot_name, pos);
++			return ISNS_INVALID_REGISTRATION;
++		}
++		attr = st->orig_attrs.ial_data[pos];
++
++		/* Make sure key attrs are complete and in order */
++		if (attr->ia_tag_id != st->tmpl->iot_keys[i]) {
++			isns_debug_protocol("%s: incomplete %s object "
++					"(key attr %u missing)\n",
++					__FUNCTION__, st->tmpl->iot_name, pos);
++			return ISNS_INVALID_REGISTRATION;
++		}
++
++		isns_attr_list_append_attr(&st->keys, attr);
++	}
++
++	/*
++	 * Consume all non-key attributes corresponding to the
++	 * object class. We stop whenever we hit another
++	 * key attribute, or an attribute that does not belong to
++	 * the object type (eg when a storage node is followed by
++	 * a PGT attribute, as described in section 5.6.5.1).
++	 */
++copy_attrs:
++	while (pos < st->orig_attrs.ial_count) {
++		uint32_t	tag;
++
++		attr = st->orig_attrs.ial_data[pos];
++		tag = attr->ia_tag_id;
++
++		if (!isns_object_attr_valid(st->tmpl, tag)
++		 || isns_object_template_find(tag) != NULL)
++			break;
++
++		pos++;
++		isns_attr_list_append_attr(&st->attrs, attr);
++	}
++	st->pos = pos;
++
++	return ISNS_SUCCESS;
++}
++
++int
++isns_attr_list_scanner_get_pg(struct isns_attr_list_scanner *st)
++{
++	isns_attr_t	*attr, *next = NULL;
++	unsigned int	pos = st->pos;
++
++
++	attr = st->orig_attrs.ial_data[st->pos++];
++	if (st->pgt_next_attr == ISNS_TAG_PG_TAG) {
++		isns_object_t	*base = st->pgt_base_object;
++
++		if (ISNS_ATTR_IS_NIL(attr))
++			st->pgt_value = 0;
++		else if (ISNS_ATTR_IS_UINT32(attr))
++			st->pgt_value = attr->ia_value.iv_uint32;
++		else
++			return ISNS_INVALID_REGISTRATION;
++
++		if (ISNS_IS_PORTAL(base)
++		 && isns_portal_from_object(&st->pgt_portal_info,
++					ISNS_TAG_PORTAL_IP_ADDRESS,
++					ISNS_TAG_PORTAL_TCP_UDP_PORT,
++					base)) {
++			st->pgt_next_attr = ISNS_TAG_PG_ISCSI_NAME;
++		} else
++		if (ISNS_IS_ISCSI_NODE(base)
++		 && isns_object_get_string(base,
++					ISNS_TAG_ISCSI_NAME,
++					&st->pgt_iscsi_name)) {
++			st->pgt_next_attr = ISNS_TAG_PORTAL_IP_ADDRESS;
++		} else {
++			return ISNS_INTERNAL_ERROR;
++		}
++
++		/* Trailing PGT at end of list. Shrug. */
++		if (st->pos >= st->orig_attrs.ial_count)
++			return ISNS_NO_SUCH_ENTRY;
++
++		attr = st->orig_attrs.ial_data[st->pos++];
++		if (attr->ia_tag_id != st->pgt_next_attr) {
++			/* Some clients may do this; catch them so
++			 * we can fix it. */
++			isns_error("Oops, client sends PGT followed by <%s>\n",
++					attr->ia_tag->it_name);
++			return ISNS_INVALID_REGISTRATION;
++		}
++	}
++
++	st->tmpl = &isns_iscsi_pg_template;
++	if (st->pgt_next_attr == ISNS_TAG_PG_ISCSI_NAME) {
++		isns_attr_list_append_attr(&st->keys, attr);
++		isns_portal_to_attr_list(&st->pgt_portal_info,
++					ISNS_TAG_PG_PORTAL_IP_ADDR,
++					ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
++					&st->keys);
++	} else
++	if (st->pgt_next_attr == ISNS_TAG_PG_PORTAL_IP_ADDR) {
++		if (st->pos >= st->orig_attrs.ial_count)
++			return ISNS_INVALID_REGISTRATION;
++
++		next = st->orig_attrs.ial_data[st->pos++];
++		if (next->ia_tag_id != ISNS_TAG_PORTAL_TCP_UDP_PORT)
++			return ISNS_INVALID_REGISTRATION;
++
++		isns_attr_list_append_string(&st->keys,
++					ISNS_TAG_PG_ISCSI_NAME,
++					st->pgt_iscsi_name);
++		isns_attr_list_append_attr(&st->keys, attr);
++		isns_attr_list_append_attr(&st->keys, next);
++	} else {
++		return ISNS_INTERNAL_ERROR;
++	}
++
++	isns_attr_list_append_uint32(&st->attrs,
++				ISNS_TAG_PG_TAG,
++				st->pgt_value);
++
++	/* Copy other PG attributes if present */
++	for (pos = st->pos; pos < st->orig_attrs.ial_count; ++pos) {
++		uint32_t	tag;
++
++		attr = st->orig_attrs.ial_data[pos];
++		tag = attr->ia_tag_id;
++
++		/*
++		 * Additional sets of PGTs and PG iSCSI Names to be
++		 * associated to the registered Portal MAY follow.
++		 */
++		if (tag == ISNS_TAG_PG_TAG) {
++			st->pgt_next_attr = tag;
++			break;
++		}
++
++		if (tag == ISNS_TAG_PG_ISCSI_NAME
++		 || tag == ISNS_TAG_PG_PORTAL_IP_ADDR
++		 || tag == ISNS_TAG_PG_PORTAL_TCP_UDP_PORT
++		 || !isns_object_attr_valid(st->tmpl, tag))
++			break;
++
++		isns_attr_list_append_attr(&st->attrs, attr);
++	}
++	st->pos = pos;
++
++	return ISNS_SUCCESS;
++}
++
++/*
++ * Get the name of a function
++ */
++#define __ISNS_MAX_FUNCTION	16
++static const char *	isns_req_function_names[__ISNS_MAX_FUNCTION] = {
++[ISNS_DEVICE_ATTRIBUTE_REGISTER]= "DevAttrReg",
++[ISNS_DEVICE_ATTRIBUTE_QUERY]	= "DevAttrQry",
++[ISNS_DEVICE_GET_NEXT]		= "DevGetNext",
++[ISNS_DEVICE_DEREGISTER]	= "DevDereg",
++[ISNS_SCN_REGISTER]		= "SCNReg",
++[ISNS_SCN_DEREGISTER]		= "SCNDereg",
++[ISNS_SCN_EVENT]		= "SCNEvent",
++[ISNS_STATE_CHANGE_NOTIFICATION]= "SCN",
++[ISNS_DD_REGISTER]		= "DDReg",
++[ISNS_DD_DEREGISTER]		= "DDDereg",
++[ISNS_DDS_REGISTER]		= "DDSReg",
++[ISNS_DDS_DEREGISTER]		= "DDSDereg",
++[ISNS_ENTITY_STATUS_INQUIRY]	= "ESI",
++[ISNS_HEARTBEAT]		= "Heartbeat",
++};
++static const char *	isns_resp_function_names[__ISNS_MAX_FUNCTION] = {
++[ISNS_DEVICE_ATTRIBUTE_REGISTER]= "DevAttrRegResp",
++[ISNS_DEVICE_ATTRIBUTE_QUERY]	= "DevAttrQryResp",
++[ISNS_DEVICE_GET_NEXT]		= "DevGetNextResp",
++[ISNS_DEVICE_DEREGISTER]	= "DevDeregResp",
++[ISNS_SCN_REGISTER]		= "SCNRegResp",
++[ISNS_SCN_DEREGISTER]		= "SCNDeregResp",
++[ISNS_SCN_EVENT]		= "SCNEventResp",
++[ISNS_STATE_CHANGE_NOTIFICATION]= "SCNResp",
++[ISNS_DD_REGISTER]		= "DDRegResp",
++[ISNS_DD_DEREGISTER]		= "DDDeregResp",
++[ISNS_DDS_REGISTER]		= "DDSRegResp",
++[ISNS_DDS_DEREGISTER]		= "DDSDeregResp",
++[ISNS_ENTITY_STATUS_INQUIRY]	= "ESIRsp",
++/* No response code for heartbeat */
++};
++
++const char *
++isns_function_name(uint32_t function)
++{
++	static char	namebuf[32];
++	const char	**names, *name;
++	unsigned int	num = function;
++
++	names = isns_req_function_names;
++	if (num & 0x8000) {
++		names = isns_resp_function_names;
++		num &= 0x7fff;
++	}
++	name = NULL;
++	if (num < __ISNS_MAX_FUNCTION)
++		name = names[num];
++	if (name == NULL) {
++		snprintf(namebuf, sizeof(namebuf),
++				"<function %08x>",
++				function);
++		name = namebuf;
++	}
++
++	return name;
++}
++
+diff --git a/utils/open-isns/slp.c b/utils/open-isns/slp.c
+new file mode 100644
+index 0000000..43075b3
+--- /dev/null
++++ b/utils/open-isns/slp.c
+@@ -0,0 +1,242 @@
++/*
++ * SLP registration and query of iSNS
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include "config.h"
++#include <stdlib.h>
++#ifdef HAVE_SLP_H
++# include <slp.h>
++#endif
++
++#include "isns.h"
++#include "util.h"
++#include "internal.h"
++
++#define ISNS_SLP_SERVICE_NAME	"iscsi:sms"
++/*
++ * RFC 4018 says we would use scope initiator-scope-list.
++ * But don't we want targets to find the iSNS server, too?
++ */
++#define ISNS_SLP_SCOPE		"initiator-scope-list"
++
++#ifdef WITH_SLP
++
++struct isns_slp_url_state {
++	SLPError	slp_err;
++	char *		slp_url;
++};
++
++static void
++isns_slp_report(SLPHandle handle, SLPError err, void *cookie)
++{
++	*(SLPError *) cookie = err;
++}
++
++/*
++ * Register a service with SLP
++ */ 
++int
++isns_slp_register(const char *url)
++{
++	SLPError	err, callbackerr; 
++	SLPHandle	handle = NULL; 
++
++	err = SLPOpen("en", SLP_FALSE, &handle); 
++	if(err != SLP_OK) { 
++		isns_error("Unable to obtain SLP handle (err %d)\n", err);
++		return 0;
++	} 
++
++	err = SLPReg(handle, url, SLP_LIFETIME_MAXIMUM,
++			ISNS_SLP_SCOPE,
++			"(description=iSNS Server),(protocols=isns)",
++			SLP_TRUE,
++			isns_slp_report, &callbackerr);
++
++	SLPClose(handle);
++
++	if (err == SLP_OK)
++		err = callbackerr;
++	if (err != SLP_OK) {
++		isns_error("Failed to register with SLP (err %d)\n", err);
++		return 0;
++	}
++
++	return 1;
++}
++
++/*
++ * DeRegister a service
++ */ 
++int
++isns_slp_unregister(const char *url)
++{
++	SLPError	err, callbackerr; 
++	SLPHandle	handle = NULL; 
++
++	isns_debug_general("SLP: Unregistering \"%s\"\n", url);
++
++	err = SLPOpen("en", SLP_FALSE, &handle); 
++	if(err != SLP_OK) { 
++		isns_error("Unable to obtain SLP handle (err %d)\n", err);
++		return 0;
++	} 
++
++	err = SLPDereg(handle, url, isns_slp_report, &callbackerr);
++
++	SLPClose(handle);
++
++	if (err == SLP_OK)
++		err = callbackerr;
++	if (err != SLP_OK) {
++		isns_error("Failed to deregister with SLP (err %d)\n", err);
++		return 0;
++	}
++
++	return 1;
++}
++
++/*
++ * Find an iSNS server through SLP
++ */
++static SLPBoolean
++isns_slp_url_callback(SLPHandle handle,
++		const char *url, unsigned short lifetime,
++		SLPError err, void *cookie) 
++{
++	struct isns_slp_url_state *sp = cookie;
++	SLPSrvURL	*parsed_url = NULL;
++	int		want_more = SLP_TRUE;
++	char		buffer[1024];
++
++	if (err != SLP_OK && err != SLP_LAST_CALL)
++		return SLP_FALSE;
++
++	if (!url)
++		goto out;
++
++	isns_debug_general("SLP: Found URL \"%s\"\n", url);
++	err = SLPParseSrvURL(url, &parsed_url);
++	if (err != SLP_OK) {
++		isns_error("Error parsing SLP service URL \"%s\"\n", url);
++		goto out;
++	}
++
++	if (parsed_url->s_pcNetFamily
++	 && parsed_url->s_pcNetFamily[0]
++	 && strcasecmp(parsed_url->s_pcNetFamily, "ip")) {
++		isns_error("Ignoring SLP service URL \"%s\"\n", url);
++		goto out;
++	}
++
++	if (parsed_url->s_iPort) {
++		snprintf(buffer, sizeof(buffer), "%s:%u",
++				parsed_url->s_pcHost,
++				parsed_url->s_iPort);
++		isns_assign_string(&sp->slp_url, buffer);
++	} else {
++		isns_assign_string(&sp->slp_url,
++				parsed_url->s_pcHost);
++	}
++	want_more = SLP_FALSE;
++
++out:
++	if (parsed_url)
++		SLPFree(parsed_url);
++	sp->slp_err = SLP_OK;
++
++	return want_more;
++}
++
++/*
++ * Locate the iSNS server using SLP.
++ * This is not really an instantaneous process. Maybe we could
++ * speed this up by using a cache.
++ */
++char *
++isns_slp_find(void)
++{
++	static struct isns_slp_url_state state;
++	SLPHandle	handle = NULL; 
++	SLPError	err; 
++
++	if (state.slp_url)
++		return state.slp_url;
++
++	isns_debug_general("Using SLP to locate iSNS server\n");
++
++	err = SLPOpen("en", SLP_FALSE, &handle); 
++	if(err != SLP_OK) { 
++		isns_error("Unable to obtain SLP handle (err %d)\n", err);
++		return NULL;
++	} 
++
++	err = SLPFindSrvs(handle, ISNS_SLP_SERVICE_NAME,
++			NULL, "(protocols=isns)",
++			isns_slp_url_callback, &state);
++
++	SLPClose(handle);
++
++	if (err == SLP_OK)
++		err = state.slp_err;
++	if (err != SLP_OK) {
++		isns_error("Failed to find service in SLP (err %d)\n", err);
++		return NULL;
++	}
++
++	if (state.slp_url == NULL) {
++		isns_error("Service %s not registered with SLP\n",
++				ISNS_SLP_SERVICE_NAME);
++		return NULL;
++
++	}
++
++	isns_debug_general("Using iSNS server at %s\n", state.slp_url);
++	return state.slp_url;
++}
++
++#else /* WITH_SLP */
++
++int
++isns_slp_register(const char *url)
++{
++	isns_error("SLP support disabled in this build\n");
++	return 0;
++}
++
++int
++isns_slp_unregister(const char *url)
++{
++	isns_error("SLP support disabled in this build\n");
++	return 0;
++}
++
++char *
++isns_slp_find(void)
++{
++	isns_error("SLP support disabled in this build\n");
++	return NULL;
++}
++
++#endif /* WITH_SLP */
++
++char *
++isns_slp_build_url(uint16_t port)
++{
++	char	buffer[1024];
++
++	if (port)
++		snprintf(buffer, sizeof(buffer),
++			"service:%s://%s:%u",
++			ISNS_SLP_SERVICE_NAME,
++			isns_config.ic_host_name, port);
++	else
++		snprintf(buffer, sizeof(buffer),
++			"service:%s://%s",
++			ISNS_SLP_SERVICE_NAME,
++			isns_config.ic_host_name);
++	return isns_strdup(buffer);
++}
++
+diff --git a/utils/open-isns/socket.c b/utils/open-isns/socket.c
+new file mode 100644
+index 0000000..47481c6
+--- /dev/null
++++ b/utils/open-isns/socket.c
+@@ -0,0 +1,2304 @@
++/*
++ * Socket handling code
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <sys/socket.h>
++#include <sys/poll.h>
++#include <sys/time.h>
++#include <sys/un.h>
++#include <string.h>
++#include <stdlib.h>
++#include <signal.h>
++#include <stdio.h>
++#include <stdarg.h>
++#include <time.h>
++#include <unistd.h>
++#include <errno.h>
++#include <netdb.h>
++#include <fcntl.h>
++
++#include "buffer.h"
++#include "isns.h"
++#include "socket.h"
++#include "security.h"
++#include "util.h"
++#include "config.h"
++
++#define SOCK_DEBUG_VERBOSE	0
++
++#ifndef AI_ADDRCONFIG
++# define AI_ADDRCONFIG		0
++#endif
++#ifndef AI_V4MAPPED
++# define AI_V4MAPPED		0
++#endif
++
++enum {
++	ISNS_MSG_DISCARD,
++	ISNS_MSG_DONE,
++	ISNS_MSG_RETURN
++};
++
++static isns_socket_t *__isns_create_socket(struct addrinfo *src,
++			struct addrinfo *dst,
++			int sock_type);
++static struct addrinfo *isns_get_address_list(const char *, const char *,
++			int, int, int);
++static void	release_addrinfo(struct addrinfo *);
++static void	isns_net_dgram_recv(isns_socket_t *);
++static void	isns_net_dgram_xmit(isns_socket_t *);
++static void	isns_net_stream_accept(isns_socket_t *);
++static void	isns_net_stream_recv(isns_socket_t *);
++static void	isns_net_stream_xmit(isns_socket_t *);
++static void	isns_net_stream_hup(isns_socket_t *);
++static void	isns_net_stream_error(isns_socket_t *, int);
++static void	isns_net_stream_reconnect(isns_socket_t *);
++static void	isns_net_stream_disconnect(isns_socket_t *);
++static isns_socket_t *isns_net_alloc(int);
++static int	isns_socket_open(isns_socket_t *);
++static int	isns_socket_queue_message(isns_socket_t *, isns_message_t *);
++static int	isns_socket_retransmit_queued(isns_socket_t *);
++
++static ISNS_LIST_DECLARE(all_sockets);
++
++#define debug_verbose(args ...) do { \
++	if (SOCK_DEBUG_VERBOSE >= 1) isns_debug_socket(args); \
++} while (0)
++#define debug_verbose2(args ...) do { \
++	if (SOCK_DEBUG_VERBOSE >= 2) isns_debug_socket(args); \
++} while (0)
++
++/*
++ * Helper function for looking at incoming PDUs
++ */
++static inline buf_t *
++isns_socket_next_pdu(isns_socket_t *sock)
++{
++	buf_t		*bp = sock->is_recv_buf;
++	unsigned int	avail;
++	struct isns_hdr	*hdr;
++	uint32_t	pdu_len = 0;
++
++	if (bp == NULL)
++		return NULL;
++
++	avail = buf_avail(bp);
++	if (avail < sizeof(*hdr))
++		return NULL;
++	hdr = buf_head(bp);
++	pdu_len = sizeof(*hdr) + ntohs(hdr->i_length);
++
++	if (avail < pdu_len)
++		return NULL;
++
++	/* Check for presence of authentication block */
++	if (hdr->i_flags & htons(ISNS_F_AUTHBLK_PRESENT)) {
++		uint32_t	*authblk, authlen;
++
++		authblk = (uint32_t *) ((char *) hdr + pdu_len);
++		if (avail < pdu_len + ISNS_AUTHBLK_SIZE)
++			return NULL;
++
++		authlen = ntohl(authblk[1]);
++		if (authlen < 20 || authlen > ISNS_MAX_MESSAGE) {
++			/* The authblock is garbage.
++			 * The only reliable way to signal such a problem
++			 * is by dropping the connection.
++			 */
++			isns_error("socket error: bad auth block\n");
++			sock->is_state = ISNS_SOCK_DEAD;
++			return NULL;
++		}
++
++		pdu_len += authlen;
++		if (avail < pdu_len)
++			return NULL;
++	}
++
++	return buf_split(&sock->is_recv_buf, pdu_len);
++}
++
++/*
++ * Try to assemble the message from PDUs
++ */
++static inline int
++isns_msg_complete(struct isns_partial_msg *msg)
++{
++	buf_t	*msg_buf, **chain, *bp;
++
++	/* Return if we haven't seen first and last frag */
++	if (((~msg->imp_flags) & (ISNS_F_FIRST_PDU|ISNS_F_LAST_PDU)))
++		return 0;
++
++	/* Simple - unfragmented case: just move
++	 * the PDU on the chain to the payload */
++	if (msg->imp_first_seq == msg->imp_last_seq) {
++		msg->imp_payload = msg->imp_chain;
++		buf_pull(msg->imp_payload, sizeof(struct isns_hdr));
++		msg->imp_chain = NULL;
++		return 1;
++	}
++
++	/* Do we have all fragments? */
++	if (msg->imp_last_seq - msg->imp_first_seq + 1
++			!= msg->imp_pdu_count)
++		return 0;
++
++	msg_buf = buf_alloc(msg->imp_msg_size);
++
++	chain = &msg->imp_chain;
++	while ((bp = *chain) != NULL) {
++		/* Pull the header off */
++		buf_pull(bp, sizeof(struct isns_hdr));
++		buf_put(msg_buf, buf_head(bp), buf_avail(bp));
++
++		*chain = bp->next;
++		buf_free(bp);
++	}
++
++	return 0;
++}
++
++/*
++ * Clear the "partial" part of the message
++ */
++static void
++__isns_msg_clear_partial(struct isns_partial_msg *msg)
++{
++	buf_list_free(msg->imp_chain);
++	msg->imp_chain = NULL;
++}
++
++/*
++ * Add an authentication block to an outgoing PDU
++ */
++#ifdef WITH_SECURITY
++static int
++isns_pdu_seal(isns_security_t *ctx, buf_t *pdu)
++{
++	struct isns_authblk	auth;
++	isns_principal_t	*self;
++
++	if (!(self = ctx->is_self)) {
++		isns_error("Cannot sign PDU: no sender identity for socket\n");
++		return 0;
++	}
++
++	auth.iab_bsd = ctx->is_type;
++	auth.iab_timestamp = time(NULL);
++	auth.iab_spi = self->is_name;
++	auth.iab_spi_len = strlen(self->is_name);
++
++	if (!isns_security_sign(ctx, self, pdu, &auth)) {
++		isns_error("Cannot sign PDU: error creating signature\n");
++		return 0;
++	}
++
++	auth.iab_length = ISNS_AUTHBLK_SIZE +
++			auth.iab_spi_len +
++			auth.iab_sig_len;
++	if (!isns_authblock_encode(pdu, &auth))
++		return 0;
++
++	isns_debug_message("Successfully signed message (authlen=%u, spilen=%u, siglen=%u)\n",
++			auth.iab_length, auth.iab_spi_len, auth.iab_sig_len);
++
++	return 1;
++}
++
++/*
++ * Authenticate a PDU
++ *
++ * The RFC is doing a bit of handwaving around the
++ * authentication issue. For example, it never
++ * spells out exactly which parts of the message
++ * are included in the SHA1 hash to be signed.
++ *
++ * It also says that the auth block "is identical in format
++ * to the SLP authentication block", but all fields
++ * are twice as wide.
++ *
++ * There's not even an error code to tell the client
++ * we were unable to authenticate him :-(
++ *
++ * Interoperability problems, here I come...
++ */
++static int
++isns_pdu_authenticate(isns_security_t *sec,
++		struct isns_partial_msg *msg, buf_t *bp)
++{
++	struct isns_hdr		*hdr = buf_head(bp);
++	unsigned int		pdu_len, avail;
++	struct isns_authblk	authblk;
++	isns_principal_t *	peer = NULL;
++	buf_t			auth_buf;
++
++	isns_debug_auth("Message has authblock; trying to authenticate\n");
++
++	/* In the TCP path, we checked this before, but
++	 * better safe than sorry. */
++	avail = buf_avail(bp);
++	pdu_len = sizeof(*hdr) + ntohs(hdr->i_length);
++	if (avail < pdu_len + ISNS_AUTHBLK_SIZE) {
++		isns_debug_auth("authblock truncated\n");
++		return 0;
++	}
++
++	/* Get the auth block */
++	buf_set(&auth_buf, buf_head(bp) + pdu_len, avail - pdu_len);
++	if (!isns_authblock_decode(&auth_buf, &authblk)) {
++		isns_debug_auth("error decoding authblock\n");
++		return 0;
++	}
++
++	/* Truncate the buffer (this just sets the
++	 * tail pointer, but doesn't free memory */
++	if (!buf_truncate(bp, pdu_len)) {
++		isns_debug_auth("buf_truncate failed - cosmic particles?\n");
++		return 0;
++	}
++
++	/* If the socket doesn't have a security context,
++	 * just ignore the auth block. */
++	if (sec == NULL) {
++		msg->imp_header.i_flags &= ~ISNS_F_AUTHBLK_PRESENT;
++		return 1;
++	}
++
++	if (authblk.iab_bsd != sec->is_type)
++		goto failed;
++
++	peer = isns_get_principal(sec, authblk.iab_spi, authblk.iab_spi_len);
++	if (peer == NULL) {
++		/* If the admin allows unknown peers, we must make
++		 * sure, however, to not allow an unauthenticated
++		 * PDU to be inserted into an authenticated message.
++		 */
++		if (isns_config.ic_auth.allow_unknown_peers
++		 && msg->imp_security == NULL) {
++			isns_debug_message(
++				"Accepting unknown peer spi=\"%.*s\" as "
++				"anonymous peer\n",
++				authblk.iab_spi_len, authblk.iab_spi);
++			return 1;
++		}
++
++		isns_debug_message(
++			"Unable to create security peer for spi=%.*s\n",
++			authblk.iab_spi_len, authblk.iab_spi);
++
++		goto failed;
++	}
++
++	if (!isns_security_verify(sec, peer, bp, &authblk)) {
++		/* Authentication failed */
++		goto failed;
++	}
++
++	/* The RFC doesn't say how to deal with fragmented
++	 * messages with different BSDs or SPIs.
++	 * kickban seems the right approach.
++	 * We discard this segment rather than failing
++	 * the entire message.
++	 */
++	if (msg->imp_chain == NULL) {
++		msg->imp_security = peer;
++		peer->is_users++;
++	} else
++	if (msg->imp_security != peer) {
++		goto failed;
++	}
++
++	isns_principal_free(peer);
++	return 1;
++
++failed:
++	isns_principal_free(peer);
++	return 0;
++}
++#else /* WITH_SECURITY */
++static int
++isns_pdu_authenticate(isns_security_t *sec,
++		struct isns_partial_msg *msg, buf_t *bp)
++{
++	return 0;
++}
++
++#endif
++
++/*
++ * Enqueue an incoming PDU on the socket.
++ *
++ * A single iSNS message may be split up into
++ * several PDUs, so we need to perform
++ * reassembly here.
++ *
++ * This function also verifies the authentication
++ * block, if present.
++ */
++static void
++isns_pdu_enqueue(isns_socket_t *sock,
++		struct sockaddr_storage *addr, socklen_t alen,
++		buf_t *segment, struct ucred *creds)
++{
++	isns_message_queue_t *q = &sock->is_partial;
++	struct isns_partial_msg *msg;
++	buf_t		**chain, *bp;
++	struct isns_hdr	*hdr;
++	uint32_t	xid, seq, flags;
++
++	hdr = (struct isns_hdr *) buf_head(segment);
++	xid = ntohs(hdr->i_xid);
++	seq = ntohs(hdr->i_seq);
++	flags = ntohs(hdr->i_flags);
++
++	isns_debug_socket("Incoming PDU xid=%04x seq=%u len=%u func=%s%s%s%s%s%s\n",
++			xid, seq, ntohs(hdr->i_length),
++			isns_function_name(ntohs(hdr->i_function)),
++			(flags & ISNS_F_CLIENT)? " client" : "",
++			(flags & ISNS_F_SERVER)? " server" : "",
++			(flags & ISNS_F_AUTHBLK_PRESENT)? " authblk" : "",
++			(flags & ISNS_F_FIRST_PDU)? " first" : "",
++			(flags & ISNS_F_LAST_PDU)? " last" : "");
++
++	/* Find the message matching (addr, xid) */
++	msg = (struct isns_partial_msg *) isns_message_queue_find(q, xid, addr, alen);
++	if (msg != NULL) {
++		if (msg->imp_creds
++		 && (!creds || memcmp(msg->imp_creds, creds, sizeof(*creds)))) {
++			isns_warning("socket: credentials mismatch! Dropping PDU\n");
++			goto drop;
++		}
++		hdr = &msg->imp_header;
++		goto found;
++	}
++
++	msg = (struct isns_partial_msg *) __isns_alloc_message(xid, sizeof(*msg),
++			(void (*)(isns_message_t *)) __isns_msg_clear_partial);
++	memcpy(&msg->imp_addr, addr, alen);
++	msg->imp_addrlen = alen;
++
++	msg->imp_header = *hdr;
++	msg->imp_header.i_seq = 0;
++
++	isns_message_queue_append(q, &msg->imp_base);
++	isns_message_release(&msg->imp_base);
++	/* Message is owned by is_partial now */
++
++	/* Fix up the PDU header */
++	hdr = &msg->imp_header;
++	hdr->i_version = ntohs(hdr->i_version);
++	hdr->i_function = ntohs(hdr->i_function);
++	hdr->i_length = ntohs(hdr->i_length);
++	hdr->i_flags = ntohs(hdr->i_flags);
++	hdr->i_xid = ntohs(hdr->i_xid);
++	hdr->i_seq = ntohs(hdr->i_seq);
++
++	if (creds) {
++		msg->imp_credbuf = *creds;
++		msg->imp_creds = &msg->imp_credbuf;
++	}
++
++found:
++	if (flags & ISNS_F_AUTHBLK_PRESENT) {
++		/* When authentication fails - should we drop the
++		 * message or treat it as unauthenticated?
++		 * For now we drop it, but a more user friendly 
++		 * approach might be to just treat it as
++		 * unauthenticated.
++		 */
++		if (!isns_pdu_authenticate(sock->is_security, msg, segment))
++			goto drop;
++	} else
++	if (msg->imp_header.i_flags & ISNS_F_AUTHBLK_PRESENT) {
++		/* Oops, unauthenticated fragment in an
++		 * authenticated message. */
++		isns_debug_message(
++			"Oops, unauthenticated fragment in an "
++			"authenticated message!\n");
++		goto drop;
++	}
++
++	if ((flags & ISNS_F_FIRST_PDU)
++	 && !(msg->imp_flags & ISNS_F_FIRST_PDU)) {
++		/* FIXME: first seq must be zero */
++		msg->imp_first_seq = seq;
++		msg->imp_flags |= ISNS_F_FIRST_PDU;
++	}
++	if ((flags & ISNS_F_LAST_PDU)
++	 && !(msg->imp_flags & ISNS_F_LAST_PDU)) {
++		msg->imp_last_seq = seq;
++		msg->imp_flags |= ISNS_F_LAST_PDU;
++	}
++
++	chain = &msg->imp_chain;
++	while ((bp = *chain) != NULL) {
++		struct isns_hdr *ohdr = buf_head(bp);
++
++		/* Duplicate? Drop it! */
++		if (seq == ohdr->i_seq)
++			goto drop;
++		if (seq < ohdr->i_seq)
++			break;
++		chain = &bp->next;
++	}
++	segment->next = *chain;
++	*chain = segment;
++
++	msg->imp_msg_size += buf_avail(segment) - sizeof(*hdr);
++	msg->imp_pdu_count++;
++
++	/* We received first and last PDU - check if the
++	 * chain is complete */
++	if (isns_msg_complete(msg)) {
++		/* Remove from partial queue.
++		 * We clean the part of the message that is
++		 * not in imp_base, so that we can pass this
++		 * to the caller and have him call
++		 * isns_message_release on it.
++		 */
++		__isns_msg_clear_partial(msg);
++
++		/* Move from partial queue to complete queue. */
++		isns_message_queue_move(&sock->is_complete,
++				&msg->imp_base);
++		msg->imp_base.im_socket = sock;
++	}
++
++	return;
++
++drop:
++	buf_free(segment);
++	return;
++}
++
++/*
++ * Send side handling
++ */
++static void
++isns_send_update(isns_socket_t *sock)
++{
++	buf_t *bp = sock->is_xmit_buf;
++
++	if (bp && buf_avail(bp) == 0) {
++		sock->is_xmit_buf = bp->next;
++		buf_free(bp);
++	}
++
++	if (sock->is_xmit_buf)
++		sock->is_poll_mask |= POLLOUT;
++	else
++		sock->is_poll_mask &= ~POLLOUT;
++}
++
++/*
++ * Close the socket
++ */
++static void
++isns_net_close(isns_socket_t *sock, int next_state)
++{
++	if (sock->is_desc >= 0) {
++		close(sock->is_desc);
++		sock->is_desc = -1;
++	}
++	sock->is_poll_mask &= ~(POLLIN|POLLOUT);
++	sock->is_state = next_state;
++
++	buf_list_free(sock->is_xmit_buf);
++	sock->is_xmit_buf = NULL;
++
++	buf_free(sock->is_recv_buf);
++	sock->is_recv_buf = NULL;
++
++	isns_message_queue_destroy(&sock->is_partial);
++	isns_message_queue_destroy(&sock->is_complete);
++}
++
++static void
++isns_net_set_timeout(isns_socket_t *sock,
++			void (*func)(isns_socket_t *),
++			unsigned int timeout)
++{
++	gettimeofday(&sock->is_deadline, NULL);
++	sock->is_deadline.tv_sec += timeout;
++	sock->is_timeout = func;
++}
++
++static void
++isns_net_cancel_timeout(isns_socket_t *sock)
++{
++	timerclear(&sock->is_deadline);
++}
++
++void
++isns_net_error(isns_socket_t *sock, int err_code)
++{
++	if (sock->is_error)
++		sock->is_error(sock, err_code);
++}
++
++/*
++ * Create a passive socket (server side)
++ */
++isns_socket_t *
++isns_create_server_socket(const char *src_spec, const char *portspec, int af_hint, int sock_type)
++{
++	struct addrinfo *src;
++
++	src = isns_get_address_list(src_spec, portspec,
++			af_hint, sock_type, AI_PASSIVE);
++	if (src == NULL)
++		return NULL;
++
++	return __isns_create_socket(src, NULL, sock_type);
++}
++
++/*
++ * Accept incoming connections.
++ */
++void
++isns_net_stream_accept(isns_socket_t *sock)
++{
++	isns_socket_t *child;
++	socklen_t optlen;
++	int	fd, passcred = 0;
++
++	fd = accept(sock->is_desc, NULL, NULL);
++	if (fd < 0) {
++		if (errno != EINTR)
++			isns_error("Error accepting connection: %m\n");
++		return;
++	}
++
++	optlen = sizeof(passcred);
++	if (getsockopt(sock->is_desc, SOL_SOCKET, SO_PASSCRED,
++				&passcred, &optlen) >= 0) {
++		setsockopt(fd, SOL_SOCKET, SO_PASSCRED,
++				&passcred, sizeof(passcred));
++	}
++
++	child = isns_net_alloc(fd);
++	child->is_type = SOCK_STREAM;
++	child->is_autoclose = 1;
++	child->is_disconnect_fatal = 1;
++	child->is_poll_in = isns_net_stream_recv;
++	child->is_poll_out = isns_net_stream_xmit;
++	child->is_poll_hup = isns_net_stream_hup;
++	child->is_error = isns_net_stream_error;
++	child->is_poll_mask = POLLIN|POLLHUP;
++	child->is_security = sock->is_security;
++
++	if (isns_config.ic_network.idle_timeout)
++		isns_net_set_timeout(child,
++			isns_net_stream_disconnect,
++			isns_config.ic_network.idle_timeout);
++
++	isns_list_append(&all_sockets, &child->is_list);
++}
++
++/*
++ * This is called from the socket code when it detects
++ * an error condition.
++ */
++static void
++isns_net_stream_error(isns_socket_t *sock, int err_code)
++{
++	int	timeo = 0, next_state = ISNS_SOCK_DEAD;
++
++	if (err_code == EAGAIN)
++		return;
++
++	isns_debug_socket("isns_net_stream_error: %s\n", strerror(err_code));
++
++	switch (err_code) {
++	case EINTR: /* ignored */
++		return;
++
++	case ECONNREFUSED:
++	case ECONNRESET:
++	case EHOSTUNREACH:
++	case ENETUNREACH:
++	case ENOTCONN:
++	case EPIPE:
++		if (sock->is_disconnect_fatal) {
++			isns_warning("socket disconnect, killing socket\n");
++			break;
++		}
++
++		/* fallthrough to disconnect */
++		timeo = isns_config.ic_network.reconnect_timeout;
++
++	case ETIMEDOUT:
++		/* Disconnect and try to reconnect */
++		if (sock->is_client) {
++			/* FIXME: We don't want this warning for ESI and
++			 * SCN sockets on the server side. */
++			isns_warning("socket disconnect, retrying in %u sec\n",
++					timeo);
++			isns_net_set_timeout(sock,
++					isns_net_stream_reconnect,
++					timeo);
++			next_state = ISNS_SOCK_DISCONNECTED;
++			break;
++		}
++
++		/* fallthru */
++
++	default:
++		isns_error("socket error: %s\n", strerror(err_code));
++	}
++
++	/* Close the socket right away */
++	isns_net_close(sock, next_state);
++}
++
++/*
++ * recvmsg wrapper handling SCM_CREDENTIALS passing
++ */
++static int
++isns_net_recvmsg(isns_socket_t *sock,
++		void *buffer, size_t count,
++		struct sockaddr *addr, socklen_t *alen,
++		struct ucred **cred)
++{
++	static struct ucred cred_buf;
++	unsigned int	control[128];
++	struct cmsghdr	*cmsg;
++	struct msghdr	msg;
++	struct iovec	iov;
++	int		len;
++
++	*cred = NULL;
++
++	iov.iov_base = buffer;
++	iov.iov_len = count;
++
++	memset(&msg, 0, sizeof(msg));
++	msg.msg_name = addr;
++	msg.msg_namelen = *alen;
++	msg.msg_iov = &iov;
++	msg.msg_iovlen = 1;
++	msg.msg_control = control;
++	msg.msg_controllen = sizeof(control);
++
++	len = recvmsg(sock->is_desc, &msg, MSG_DONTWAIT);
++
++	if (len < 0)
++		return len;
++
++	cmsg = CMSG_FIRSTHDR(&msg);
++	while (cmsg) {
++		if (cmsg->cmsg_level == SOL_SOCKET
++		 && cmsg->cmsg_type == SCM_CREDENTIALS) {
++			memcpy(&cred_buf, CMSG_DATA(cmsg), sizeof(cred_buf));
++			*cred = &cred_buf;
++			break;
++		}
++
++		cmsg = CMSG_NXTHDR(&msg, cmsg);
++	}
++
++	*alen = msg.msg_namelen;
++	return len;
++}
++
++void
++isns_net_stream_recv(isns_socket_t *sock)
++{
++	unsigned char	buffer[ISNS_MAX_BUFFER];
++	struct sockaddr_storage addr;
++	struct ucred	*creds = NULL;
++	socklen_t	alen = sizeof(addr);
++	buf_t		*bp;
++	size_t		count, total = 0;
++	int		len;
++
++again:
++	if ((bp = sock->is_recv_buf) == NULL) {
++		bp = buf_alloc(ISNS_MAX_MESSAGE);
++		sock->is_recv_buf = bp;
++	}
++
++	if ((count = buf_tailroom(bp)) > sizeof(buffer))
++		count = sizeof(buffer);
++
++	if (count == 0) {
++		/* Message too large */
++		isns_net_stream_error(sock, EMSGSIZE);
++		return;
++	}
++
++#if 0
++	len = recvfrom(sock->is_desc, buffer, count, MSG_DONTWAIT,
++			(struct sockaddr *) &addr, &alen);
++#else
++	len = isns_net_recvmsg(sock, buffer, count,
++			(struct sockaddr *) &addr, &alen,
++			&creds);
++#endif
++	if (len < 0) {
++		isns_net_stream_error(sock, errno);
++		return;
++	}
++	if (len == 0) {
++		if (total == 0)
++			sock->is_poll_mask &= ~POLLIN;
++		return;
++	}
++
++	/* We received some data from client, re-arm the
++	 * idle disconnect timer */
++	if (sock->is_autoclose
++	 && isns_config.ic_network.idle_timeout)
++		isns_net_set_timeout(sock,
++			isns_net_stream_disconnect,
++			isns_config.ic_network.idle_timeout);
++
++	buf_put(bp, buffer, len);
++	total += len;
++
++	/* Chop up the recv buffer into PDUs */
++	while ((bp = isns_socket_next_pdu(sock)) != NULL) {
++		/* We have a full PDU; enqueue it */
++		/* We shouldn't have more than one partial message
++		 * on a TCP connection; we could check this here.
++		 */
++		isns_pdu_enqueue(sock, &addr, alen, bp, creds);
++	}
++
++	goto again;
++}
++
++void
++isns_net_stream_xmit(isns_socket_t *sock)
++{
++	unsigned int	count;
++	buf_t		*bp = sock->is_xmit_buf;
++	int		len;
++
++	/* If a connecting socket can send, it has
++	 * the TCP three-way handshake. */
++	if (sock->is_state == ISNS_SOCK_CONNECTING) {
++		sock->is_state = ISNS_SOCK_IDLE;
++		sock->is_poll_mask |= POLLIN;
++		isns_net_cancel_timeout(sock);
++	}
++
++	if (bp == NULL)
++		return;
++
++	count = buf_avail(bp);
++	len = send(sock->is_desc, buf_head(bp), count, MSG_DONTWAIT);
++	if (len < 0) {
++		isns_net_stream_error(sock, errno);
++		return;
++	}
++
++	debug_verbose("isns_net_stream_xmit(%p, count=%u): transmitted %d\n",
++			sock, count, len);
++	buf_pull(bp, len);
++	isns_send_update(sock);
++}
++
++void
++isns_net_stream_hup(isns_socket_t *sock)
++{
++	sock->is_poll_mask &= ~(POLLIN|POLLOUT);
++	/* POLLHUP while connecting means we failed */
++	if (sock->is_state == ISNS_SOCK_CONNECTING)
++		isns_net_stream_error(sock, ECONNREFUSED);
++}
++
++/*
++ * Clone an addrinfo list
++ */
++static struct addrinfo *
++clone_addrinfo(const struct addrinfo *ai)
++{
++	struct addrinfo *res = NULL, **p;
++
++	p = &res;
++	for (; ai; ai = ai->ai_next) {
++		struct addrinfo *new;
++
++		if (ai->ai_addrlen > sizeof(struct sockaddr_storage))
++			continue;
++
++		new = isns_calloc(1, sizeof(*new) + ai->ai_addrlen);
++		new->ai_family = ai->ai_family;
++		new->ai_socktype = ai->ai_socktype;
++		new->ai_protocol = ai->ai_protocol;
++		new->ai_addrlen = ai->ai_addrlen;
++		new->ai_addr = (struct sockaddr *) (new + 1);
++		memcpy(new->ai_addr, ai->ai_addr, new->ai_addrlen);
++
++		*p = new;
++		p = &new->ai_next;
++	}
++
++	return res;
++}
++
++static struct addrinfo *
++__make_addrinfo(const struct sockaddr *ap, socklen_t alen, int socktype)
++{
++	struct addrinfo *new;
++
++	new = isns_calloc(1, sizeof(*new) + alen);
++	new->ai_family = ap->sa_family;
++	new->ai_socktype = socktype;
++	new->ai_protocol = 0;
++	new->ai_addrlen = alen;
++	new->ai_addr = (struct sockaddr *) (new + 1);
++	memcpy(new->ai_addr, ap, alen);
++
++	return new;
++}
++
++static struct addrinfo *
++make_addrinfo_unix(const char *pathname, int socktype)
++{
++	unsigned int	len = strlen(pathname);
++	struct sockaddr_un sun;
++
++	if (len + 1 > sizeof(sun.sun_path)) {
++		isns_error("Can't set AF_LOCAL address: path too long!\n");
++		return NULL;
++	}
++
++	sun.sun_family = AF_LOCAL;
++	strcpy(sun.sun_path, pathname);
++	return __make_addrinfo((struct sockaddr *) &sun, SUN_LEN(&sun) + 1, socktype);
++}
++
++static struct addrinfo *
++make_addrinfo_any(int family, int socktype)
++{
++	struct sockaddr_storage addr = { .ss_family = AF_UNSPEC };
++	struct addrinfo *res;
++
++	if (family != AF_UNSPEC) {
++		addr.ss_family = family;
++		res = __make_addrinfo((struct sockaddr *) &addr, sizeof(addr), socktype);
++	} else {
++		addr.ss_family = AF_INET6;
++		res = __make_addrinfo((struct sockaddr *) &addr, sizeof(addr), socktype);
++		addr.ss_family = AF_INET;
++		res->ai_next = __make_addrinfo((struct sockaddr *) &addr, sizeof(addr), socktype);
++	}
++
++	return res;
++}
++
++/*
++ * Release addrinfo created by functions above.
++ * We cannot use freeaddrinfo, as we don't know how it
++ * is implemented.
++ */
++static void
++release_addrinfo(struct addrinfo *ai)
++{
++	struct addrinfo	*next;
++
++	for (; ai; ai = next) {
++		next = ai->ai_next;
++		isns_free(ai);
++	}
++}
++
++static void
++__isns_sockaddr_set_current(struct __isns_socket_addr *info,
++		const struct addrinfo *ai)
++{
++	if (!ai)
++		return;
++
++	/* Cannot overflow; we check addrlen in clone_addrinfo */
++	memcpy(&info->addr, ai->ai_addr, ai->ai_addrlen);
++	info->addrlen = ai->ai_addrlen;
++}
++
++static void
++isns_sockaddr_init(struct __isns_socket_addr *info,
++		struct addrinfo *ai)
++{
++	if (ai == NULL)
++		return;
++
++	__isns_sockaddr_set_current(info, ai);
++
++	/* keep a copy so that we can loop through
++	 * all addrs */
++	info->list = ai;
++
++	/* Make the list circular */
++	while (ai->ai_next)
++		ai = ai->ai_next;
++	ai->ai_next = info->list;
++}
++
++static void
++isns_sockaddr_destroy(struct __isns_socket_addr *info)
++{
++	struct addrinfo *ai, *next;
++
++	if ((ai = info->list) != NULL) {
++		/* Break the circular list */
++		info->list = NULL;
++		next = ai->ai_next;
++		ai->ai_next = NULL;
++		isns_assert(next);
++
++		/* Can't use freeaddrinfo on homegrown
++		 * addrinfo lists. */
++		release_addrinfo(next);
++	}
++}
++
++static int
++isns_sockaddr_set_next(struct __isns_socket_addr *info)
++{
++	struct addrinfo *ai;
++
++	if (!(ai = info->list))
++		return 0;
++
++	info->list = ai->ai_next;
++	__isns_sockaddr_set_current(info, info->list);
++	return 1;
++}
++
++/*
++ * This function is used to pick a matching source address
++ * when connecting to some server.
++ */
++static int
++isns_sockaddr_select(struct __isns_socket_addr *info,
++		const struct sockaddr_storage *hint)
++{
++	struct addrinfo *head = info->list, *ai;
++
++	if (info->list == NULL)
++		return 0;
++
++	if (hint->ss_family == AF_INET6) {
++		struct addrinfo *good = NULL, *best = NULL;
++
++		ai = head; 
++		do {
++			if (ai->ai_family == AF_INET) {
++				/* Possible improvement: when
++				 * destination is not a private network,
++				 * prefer non-private source. */
++				good = ai;
++			} else
++			if (ai->ai_family == AF_INET6) {
++				/* Possible improvement: prefer IPv6 addr
++				 * with same address scope (local, global)
++				 */
++				best = ai;
++				break;
++			}
++
++			ai = ai->ai_next;
++		} while (ai != head);
++
++		if (!best)
++			best = good;
++		if (best) {
++			__isns_sockaddr_set_current(info, best);
++			return 1;
++		}
++	} else
++	if (hint->ss_family == AF_INET || hint->ss_family == AF_LOCAL) {
++		ai = head; 
++		do {
++			if (ai->ai_family == hint->ss_family) {
++				__isns_sockaddr_set_current(info, ai);
++				return 1;
++			}
++			ai = ai->ai_next;
++		} while (ai != head);
++	}
++
++	return 0;
++}
++
++void
++isns_net_stream_reconnect(isns_socket_t *sock)
++{
++	struct sockaddr *addr = (struct sockaddr *) &sock->is_dst.addr;
++
++	debug_verbose("isns_net_stream_reconnect(%p)\n", sock);
++
++	/* If we timed out while connecting, close the socket
++	 * and try again. */
++	if (sock->is_state == ISNS_SOCK_CONNECTING) {
++		isns_net_close(sock, ISNS_SOCK_DISCONNECTED);
++		isns_sockaddr_set_next(&sock->is_dst);
++	}
++
++	if (!isns_socket_open(sock)) {
++		isns_error("isns_net_stream_reconnect: cannot create socket\n");
++		sock->is_state = ISNS_SOCK_DEAD;
++		return;
++	}
++
++	if (connect(sock->is_desc, addr, sock->is_dst.addrlen) >= 0) {
++		sock->is_state = ISNS_SOCK_IDLE;
++		sock->is_poll_mask |= POLLIN;
++	} else 
++	if (errno == EINTR || errno == EINPROGRESS) {
++		sock->is_state = ISNS_SOCK_CONNECTING;
++		isns_net_set_timeout(sock,
++				isns_net_stream_reconnect,
++				isns_config.ic_network.connect_timeout);
++		sock->is_poll_mask |= POLLOUT;
++	} else {
++		isns_net_stream_error(sock, errno);
++		return;
++	}
++
++	/* We're connected, or in the process of doing so.
++	 * Check if there are any pending messages, and
++	 * retransmit them. */
++	isns_socket_retransmit_queued(sock);
++}
++
++void
++isns_net_stream_disconnect(isns_socket_t *sock)
++{
++	isns_debug_socket("Disconnecting idle socket\n");
++	isns_net_close(sock, ISNS_SOCK_DEAD);
++}
++
++/*
++ * Datagram send/recv
++ */
++static int
++isns_net_dgram_connect(isns_socket_t *sock)
++{
++	return connect(sock->is_desc,
++			(struct sockaddr *) &sock->is_dst.addr,
++			sock->is_dst.addrlen);
++}
++
++void
++isns_net_dgram_recv(isns_socket_t *sock)
++{
++	unsigned char	buffer[ISNS_MAX_BUFFER];
++	struct sockaddr_storage addr;
++	socklen_t	alen = sizeof(addr);
++	buf_t		*bp;
++	int		len;
++
++	len = recvfrom(sock->is_desc, buffer, sizeof(buffer),
++			MSG_DONTWAIT, (struct sockaddr *) &addr, &alen);
++	if (len < 0) {
++		isns_error("recv: %m\n");
++		return;
++	}
++	if (len == 0)
++		return;
++
++	bp = buf_alloc(len);
++	if (bp == NULL)
++		return;
++
++	buf_put(bp, buffer, len);
++	isns_pdu_enqueue(sock, &addr, alen, bp, NULL);
++}
++
++void
++isns_net_dgram_xmit(isns_socket_t *sock)
++{
++	unsigned int	count;
++	buf_t		*bp = sock->is_xmit_buf;
++	int		len;
++
++	count = buf_avail(bp);
++	if (bp->addrlen) {
++		len = sendto(sock->is_desc, buf_head(bp), count, MSG_DONTWAIT,
++			(struct sockaddr *) &bp->addr, bp->addrlen);
++	} else {
++		len = sendto(sock->is_desc, buf_head(bp), count, MSG_DONTWAIT,
++			NULL, 0);
++	}
++
++	/* Even if sendto failed, we will pull the pending buffer
++	 * off the send chain. Else we'll loop forever on an
++	 * unreachable host. */
++	if (len < 0)
++		isns_error("send: %m\n");
++
++	buf_pull(bp, count);
++	isns_send_update(sock);
++}
++
++/*
++ * Bind socket to random port
++ */
++static int
++__isns_socket_bind_random(int fd,
++		const struct sockaddr *orig_addr,
++		socklen_t src_len)
++{
++	struct sockaddr_storage addr;
++	struct sockaddr *src_addr;
++	uint16_t min = 888, max = 1024;
++	unsigned int loop = 0;
++
++	/* Copy the address to a writable location */
++	isns_assert(src_len <= sizeof(addr));
++	memcpy(&addr, orig_addr, src_len);
++	src_addr = (struct sockaddr *) &addr;
++
++	/* Bind to a random port */
++	do {
++		uint16_t port;
++
++		port = random();
++		port = min + (port % (max - min));
++
++		isns_addr_set_port(src_addr, port);
++		
++		if (bind(fd, src_addr, src_len) == 0)
++			return 1;
++
++		if (errno == EACCES && min < 1024) {
++			min = 1024;
++			max = 65535;
++			continue;
++		}
++	} while (errno == EADDRINUSE && ++loop < 128);
++
++	isns_error("Unable to bind socket\n");
++	return 0;
++}
++
++/*
++ * Create a socket
++ */
++isns_socket_t *
++__isns_create_socket(struct addrinfo *src, struct addrinfo *dst, int sock_type)
++{
++	isns_socket_t *sock;
++
++	sock = isns_net_alloc(-1);
++	sock->is_type = sock_type;
++	
++	/* Set address lists */
++	isns_sockaddr_init(&sock->is_dst, dst);
++	isns_sockaddr_init(&sock->is_src, src);
++
++	if (dst) {
++		/* This is an outgoing connection. */
++		sock->is_client = 1;
++
++		if (!isns_socket_open(sock))
++			goto failed;
++
++		if (sock_type == SOCK_DGRAM) {
++			sock->is_poll_in = isns_net_dgram_recv;
++			sock->is_poll_out = isns_net_dgram_xmit;
++			sock->is_poll_mask = POLLIN;
++
++			sock->is_retrans_timeout = isns_config.ic_network.udp_retrans_timeout;
++
++			while (isns_net_dgram_connect(sock) < 0) {
++				if (isns_sockaddr_set_next(&sock->is_dst)
++				 && sock->is_dst.list != dst)
++					continue;
++				isns_error("Unable to connect: %m\n");
++				goto failed;
++			}
++		} else {
++			/* Stream socket */
++			sock->is_poll_in = isns_net_stream_recv;
++			sock->is_poll_out = isns_net_stream_xmit;
++			sock->is_poll_hup = isns_net_stream_hup;
++			sock->is_error = isns_net_stream_error;
++			sock->is_poll_mask = POLLHUP;
++
++			sock->is_retrans_timeout = isns_config.ic_network.tcp_retrans_timeout;
++
++			isns_net_stream_reconnect(sock);
++		}
++	} else {
++		if (!isns_socket_open(sock))
++			goto failed;
++
++		if (sock_type == SOCK_DGRAM) {
++			sock->is_poll_in = isns_net_dgram_recv;
++			sock->is_poll_out = isns_net_dgram_xmit;
++			sock->is_state = ISNS_SOCK_IDLE;
++		} else {
++			sock->is_poll_in = isns_net_stream_accept;
++			sock->is_error = isns_net_stream_error;
++			sock->is_state = ISNS_SOCK_LISTENING;
++		}
++		sock->is_poll_mask = POLLIN;
++	}
++
++	isns_list_append(&all_sockets, &sock->is_list);
++	return sock;
++
++failed:
++	isns_socket_free(sock);
++	return NULL;
++}
++
++/*
++ * Connect to the master process
++ */
++isns_socket_t *
++isns_create_bound_client_socket(const char *src_spec, const char *dst_spec,
++		const char *portspec, int af_hint, int sock_type)
++{
++	struct addrinfo	*src = NULL, *dst;
++
++	if (src_spec) {
++		src = isns_get_address_list(src_spec, NULL, af_hint, sock_type, 0);
++		if (src == NULL)
++			return NULL;
++	}
++
++	dst = isns_get_address_list(dst_spec, portspec, af_hint, sock_type, 0);
++	if (dst == NULL) {
++		release_addrinfo(src);
++		return NULL;
++	}
++
++	return __isns_create_socket(src, dst, sock_type);
++}
++
++isns_socket_t *
++isns_create_client_socket(const char *dst_spec, const char *portspec, int af_hint, int sock_type)
++{
++	return isns_create_bound_client_socket(NULL, dst_spec, portspec, af_hint, sock_type);
++}
++
++static inline int
++isns_socket_type_from_portal(const isns_portal_info_t *info)
++{
++	switch (info->proto) {
++	case IPPROTO_TCP:
++		return SOCK_STREAM;
++	case IPPROTO_UDP:
++		return SOCK_DGRAM;
++	default:
++		isns_error("Unknown protocol %d in portal\n", info->proto);
++	}
++	return -1;
++}
++
++isns_socket_t *
++isns_connect_to_portal(const isns_portal_info_t *info)
++{
++	struct sockaddr_storage dst_addr;
++	struct addrinfo *ai;
++	int dst_alen, sock_type;
++
++	if ((sock_type = isns_socket_type_from_portal(info)) < 0)
++		return NULL;
++
++	dst_alen = isns_portal_to_sockaddr(info, &dst_addr);
++	ai = __make_addrinfo((struct sockaddr *) &dst_addr, dst_alen, sock_type);
++
++	return __isns_create_socket(NULL, ai, sock_type);
++}
++
++/*
++ * Make server side disconnects isns_fatal.
++ * Nice for command line apps.
++ */
++void
++isns_socket_set_disconnect_fatal(isns_socket_t *sock)
++{
++	sock->is_disconnect_fatal = 1;
++}
++
++void
++isns_socket_set_report_failure(isns_socket_t *sock)
++{
++	sock->is_report_failure = 1;
++}
++
++/*
++ * Set the socket's security context
++ */
++void
++isns_socket_set_security_ctx(isns_socket_t *sock,
++				isns_security_t *ctx)
++{
++	sock->is_security = ctx;
++}
++
++/*
++ * Create a socket
++ */
++static isns_socket_t *
++isns_net_alloc(int fd)
++{
++	isns_socket_t *new;
++
++	new = isns_calloc(1, sizeof(*new));
++	new->is_desc = fd;
++	if (fd >= 0)
++		new->is_state = ISNS_SOCK_IDLE;
++	else
++		new->is_state = ISNS_SOCK_DISCONNECTED;
++
++	isns_message_queue_init(&new->is_partial);
++	isns_message_queue_init(&new->is_complete);
++	isns_message_queue_init(&new->is_pending);
++	isns_list_init(&new->is_list);
++
++	return new;
++}
++
++/*
++ * Open the socket
++ */
++static int
++isns_socket_open(isns_socket_t *sock)
++{
++	int	af, fd, state = ISNS_SOCK_IDLE;
++
++	if (sock->is_desc >= 0)
++		return 1;
++
++	af = sock->is_dst.addr.ss_family;
++	if (af != AF_UNSPEC) {
++		/* Select a matching source address */
++		if (sock->is_src.list
++		 && !isns_sockaddr_select(&sock->is_src, &sock->is_dst.addr)) {
++			isns_warning("No matching source address for given destination\n");
++			return 0;
++		}
++	} else {
++		af = sock->is_src.addr.ss_family;
++		if (af == AF_UNSPEC)
++			return 0;
++	}
++
++	if ((fd = socket(af, sock->is_type, 0)) < 0) {
++		isns_error("Unable to create socket: %m\n");
++		return 0;
++	}
++
++	if (sock->is_src.addr.ss_family != AF_UNSPEC) {
++		const struct sockaddr *src_addr;
++		int	src_len, on = 1, bound = 0;
++
++		src_addr = (struct sockaddr *) &sock->is_src.addr;
++		src_len = sock->is_src.addrlen;
++
++		/* For debugging only! */
++		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
++			isns_error("setsockopt(SO_REUSEADDR) failed: %m\n");
++			goto failed;
++		}
++
++		switch (af) {
++		case AF_LOCAL:
++			unlink(((struct sockaddr_un *) src_addr)->sun_path);
++
++			if (sock->is_type == SOCK_STREAM
++			 && setsockopt(fd, SOL_SOCKET, SO_PASSCRED,
++				 		&on, sizeof(on)) < 0) {
++				isns_error("setsockopt(SO_PASSCRED) failed: %m\n");
++				goto failed;
++			}
++			break;
++
++		case AF_INET:
++		case AF_INET6:
++			if (isns_addr_get_port(src_addr) == 0) {
++				if (!__isns_socket_bind_random(fd, src_addr, src_len))
++					goto failed;
++				bound++;
++			}
++			break;
++		}
++
++		if (!bound && bind(fd, src_addr, src_len) < 0) {
++			isns_error("Unable to bind socket: %m\n");
++			goto failed;
++		}
++	}
++
++	if (sock->is_client) {
++		/* Set to nonblocking behavior; makes the connect
++		 * call return instantly. */
++		fcntl(fd, F_SETFL, O_NONBLOCK);
++	} else {
++		if (sock->is_type == SOCK_STREAM) {
++			if (listen(fd, 128) < 0) {
++				isns_error("Unable to listen on socket: %m\n");
++				goto failed;
++			}
++			state = ISNS_SOCK_LISTENING;
++		}
++	}
++
++	sock->is_desc = fd;
++	sock->is_state = state;
++	return 1;
++
++failed:
++	close(fd);
++	return 0;
++}
++
++/*
++ * Destroy a socket
++ */
++static inline void
++isns_socket_destroy(isns_socket_t *sock)
++{
++	isns_sockaddr_destroy(&sock->is_dst);
++	isns_sockaddr_destroy(&sock->is_src);
++	isns_free(sock);
++}
++
++void
++isns_socket_free(isns_socket_t *sock)
++{
++	isns_net_close(sock, ISNS_SOCK_DEAD);
++	isns_list_del(&sock->is_list);
++
++	sock->is_destroy = 1;
++	if (sock->is_users == 0)
++		isns_socket_destroy(sock);
++}
++
++int
++isns_socket_release(isns_socket_t *sock)
++{
++	isns_assert(sock->is_users);
++	sock->is_users -= 1;
++
++	if (sock->is_destroy) {
++		if (!sock->is_users)
++			isns_socket_destroy(sock);
++		return 0;
++	}
++	return 1;
++}
++
++/*
++ * Display a socket
++ */
++#if SOCK_DEBUG_VERBOSE > 0
++static const char *
++isns_socket_state_name(int state)
++{
++	static char xbuf[16];
++
++	switch (state) {
++	case ISNS_SOCK_LISTENING:
++		return "listening";
++	case ISNS_SOCK_CONNECTING:
++		return "connecting";
++	case ISNS_SOCK_IDLE:
++		return "idle";
++	case ISNS_SOCK_FAILED:
++		return "failed";
++	case ISNS_SOCK_DISCONNECTED:
++		return "disconnected";
++	case ISNS_SOCK_DEAD:
++		return "dead";
++	}
++	snprintf(xbuf, sizeof(xbuf), "<%u>", state);
++	return xbuf;
++}
++
++static void
++isns_print_socket(const isns_socket_t *sock)
++{
++	isns_message_t	*msg = NULL;
++	char	buffer[8192];
++	size_t	pos = 0, size = sizeof(buffer);
++
++	snprintf(buffer + pos, size - pos,
++			"socket %p desc %d state %s",
++			sock, sock->is_desc,
++			isns_socket_state_name(sock->is_state));
++	pos = strlen(buffer);
++
++	if (timerisset(&sock->is_deadline)) {
++		snprintf(buffer + pos, size - pos, " deadline=%ldms",
++				__timeout_millisec(NULL, &sock->is_deadline));
++		pos = strlen(buffer);
++	}
++
++	if ((msg = isns_message_queue_head(&sock->is_pending)) != NULL) {
++		snprintf(buffer + pos, size - pos, " msg timeout=%ldms",
++				__timeout_millisec(NULL, &msg->im_timeout));
++		pos = strlen(buffer);
++	}
++
++	isns_debug_socket("%s\n", buffer);
++}
++#else
++#define isns_print_socket(p)	do { } while (0)
++#endif
++
++/*
++ * Process incoming messages, and timeouts
++ */
++static int
++isns_net_validate(isns_socket_t *sock, isns_message_t *msg,
++		const isns_message_t *check_msg)
++{
++	isns_message_t *orig = NULL;
++	int	verdict = ISNS_MSG_DISCARD;
++
++	if (sock->is_security && !msg->im_security) {
++		/* Rude server, or malicious man in the
++		 * middle. */
++		isns_debug_message("Ignoring unauthenticated message\n");
++		goto out;
++	}
++
++	/* If this is a request, return it. */
++	if (!(msg->im_header.i_function & 0x8000)) {
++		if (check_msg == NULL) {
++			verdict = ISNS_MSG_RETURN;
++		} else {
++			/* Else: see if there's a server attached to this
++			 * socket. */
++		}
++		goto out;
++	}
++
++	orig = isns_message_queue_find(&sock->is_pending, msg->im_xid, NULL, 0);
++	if (orig == NULL) {
++		isns_debug_message("Ignoring spurious response message (xid=%04x)\n",
++				msg->im_xid);
++		goto out;
++	}
++
++	isns_message_unlink(orig);
++	if (orig->im_header.i_function != (msg->im_header.i_function & 0x7FFF)) {
++		isns_debug_message("Response message doesn't match function\n");
++		goto out;
++	}
++
++	if (check_msg == orig) {
++		verdict = ISNS_MSG_RETURN;
++	} else {
++		isns_debug_message("Received response for pending message 0x%x\n",
++				msg->im_xid);
++		if (orig->im_callback)
++			orig->im_callback(orig, msg);
++		verdict = ISNS_MSG_DONE;
++	}
++
++out:
++	isns_message_release(orig);
++	return verdict;
++}
++
++static void
++isns_net_timeout(isns_socket_t *sock, isns_message_t *msg)
++{
++	if (msg->im_callback)
++		msg->im_callback(msg, NULL);
++	isns_message_release(msg);
++}
++
++/*
++ * Helper function to update timeout
++ */
++static inline void
++__set_timeout(struct timeval *end, unsigned long timeout)
++{
++	gettimeofday(end, NULL);
++	end->tv_sec += timeout;
++}
++
++static inline int
++__timeout_expired(const struct timeval *now, const struct timeval *expires)
++{
++	/* FIXME: Should ignore sub-millisecond remainder */
++	return timercmp(now, expires, >=);
++}
++
++static long
++__timeout_millisec(const struct timeval *now, const struct timeval *expires)
++{
++	struct timeval	__now, delta = { 0, 0 };
++
++	if (now == NULL) {
++		gettimeofday(&__now, NULL);
++		now = &__now;
++	}
++
++	timersub(expires, now, &delta);
++
++	return delta.tv_sec * 1000 + delta.tv_usec / 1000;
++}
++
++static inline void
++__update_timeout(struct timeval *end, const struct timeval *timeout)
++{
++	if (!timerisset(end) || timercmp(timeout, end, <))
++		*end = *timeout;
++}
++
++/*
++ * Get the next iSNS message
++ */
++isns_message_t *
++__isns_recv_message(const struct timeval *end_time, isns_message_t *watch_msg)
++{
++	isns_socket_t	*sock, **sock_list;
++	isns_list_t	*pos, *next;
++	struct pollfd	*pfd;
++	unsigned int	i, count, max_sockets;
++	struct timeval	now, this_end;
++	int		r;
++
++	max_sockets = isns_config.ic_network.max_sockets;
++	sock_list = alloca(max_sockets * sizeof(sock_list[0]));
++	pfd = alloca(max_sockets * sizeof(pfd[0]));
++
++again:
++	timerclear(&this_end);
++	gettimeofday(&now, NULL);
++
++	if (end_time) {
++		if (__timeout_expired(&now, end_time))
++			return NULL;
++		this_end = *end_time;
++	}
++
++	i = 0;
++	isns_list_foreach(&all_sockets, pos, next) {
++		isns_socket_t	*sock = isns_list_item(isns_socket_t, is_list, pos);
++		isns_message_t	*msg = NULL;
++
++		/* We need to be a little careful here; callbacks may
++		 * mark the socket for destruction.
++		 * Bumping is_users while we're busy with the socket
++		 * prevents mayhem. */
++		sock->is_users++;
++
++		while ((msg = isns_message_dequeue(&sock->is_complete)) != NULL) {
++			switch (isns_net_validate(sock, msg, watch_msg)) {
++			case ISNS_MSG_RETURN:
++				isns_assert(!sock->is_destroy);
++				isns_socket_release(sock);
++				return msg;
++			default:
++				isns_message_release(msg);
++				isns_socket_release(sock);
++				return NULL;
++			}
++		}
++
++		isns_print_socket(sock);
++
++		/* This handles reconnect, idle disconnect etc. */
++		while (timerisset(&sock->is_deadline)) {
++			if (__timeout_expired(&now, &sock->is_deadline)) {
++				timerclear(&sock->is_deadline);
++				sock->is_timeout(sock);
++				isns_print_socket(sock);
++				continue;
++			}
++			__update_timeout(&this_end, &sock->is_deadline);
++			break;
++		}
++
++		/* No more input and output means closed&dead */
++		if (sock->is_state == ISNS_SOCK_IDLE
++		 && !(sock->is_poll_mask & (POLLIN|POLLOUT))) {
++			isns_debug_socket("connection closed by peer, killing socket\n");
++			isns_net_close(sock, ISNS_SOCK_FAILED);
++		}
++
++		/* Check whether pending messages have timed out. */
++		while ((msg = isns_message_queue_head(&sock->is_pending)) !=
++		        NULL) {
++			if (__timeout_expired(&now, &msg->im_timeout)) {
++				isns_debug_socket("sock %p message %04x timed out\n",
++						sock, msg->im_xid);
++				isns_message_unlink(msg);
++				if (msg == watch_msg) {
++					isns_message_release(msg);
++					isns_socket_release(sock);
++					return NULL;
++				}
++
++				isns_net_timeout(sock, msg);
++				continue;
++			}
++
++			if (!__timeout_expired(&now, &msg->im_resend_timeout)) {
++				__update_timeout(&this_end,
++						&msg->im_resend_timeout);
++				/* In odd configurations, the call_timeout
++				 * may be lower than the resend_timeout */
++				__update_timeout(&this_end,
++						&msg->im_timeout);
++				break;
++			}
++
++			isns_debug_socket("sock %p message %04x - "
++					"minor timeout, resending.\n",
++					sock, msg->im_xid);
++
++			/* If a TCP socket times out, something is
++			 * fishy. Force a reconnect, which will resend
++			 * all pending messages. */
++			if (sock->is_type == SOCK_STREAM) {
++				isns_net_close(sock, ISNS_SOCK_DISCONNECTED);
++				isns_net_set_timeout(sock,
++					isns_net_stream_reconnect,
++					0);
++				break;
++			}
++
++			/* UDP socket - retransmit this one message */
++			isns_message_queue_remove(&sock->is_pending, msg);
++			isns_socket_queue_message(sock, msg);
++			isns_message_release(msg);
++		}
++
++		/* 
++		 * If the socket on which we're waiting right
++		 * now got disconnected, or had any other kind of
++		 * error, return right away to let the caller know.
++		 */
++		if (sock->is_state == ISNS_SOCK_FAILED) {
++			if (sock->is_disconnect_fatal)
++				goto kill_socket;
++			if (sock->is_report_failure) {
++				isns_socket_release(sock);
++				return NULL;
++			}
++			sock->is_state = ISNS_SOCK_DISCONNECTED;
++			isns_socket_release(sock);
++			continue;
++		}
++
++		if (sock->is_state == ISNS_SOCK_DEAD) {
++kill_socket:
++			isns_list_del(&sock->is_list);
++			if (sock->is_report_failure) {
++				isns_socket_release(sock);
++				return NULL;
++			}
++			if (!sock->is_client)
++				isns_socket_free(sock);
++			isns_socket_release(sock);
++			continue;
++		}
++
++		/* This will return 0 if the socket was marked for
++		 * destruction. */
++		if (!isns_socket_release(sock))
++			continue;
++
++		/* should not happen */
++		if (i >= max_sockets)
++			break;
++
++		pfd[i].fd = sock->is_desc;
++		pfd[i].events = sock->is_poll_mask;
++		sock_list[i] = sock;
++		i++;
++	}
++	count = i;
++
++	if (timerisset(&this_end)) {
++		long		millisec;
++
++		/* timeval arithmetic can yield sub-millisecond timeouts.
++		 * Round up to prevent looping. */
++		millisec = __timeout_millisec(&now, &this_end);
++		if (millisec == 0)
++			millisec += 1;
++
++		debug_verbose2("poll(%p, %u, %d)\n", pfd, count, millisec);
++		r = poll(pfd, count, millisec);
++	} else {
++		r = poll(pfd, count, -1);
++	}
++
++	if (r < 0) {
++		if (errno != EINTR)
++			isns_error("poll returned error: %m\n");
++		return NULL;
++	}
++
++	/* Any new incoming connections will be added to the
++	 * head of the list. */
++	for (i = 0; i < count; ++i) {
++		sock = sock_list[i];
++		if (pfd[i].revents & POLLIN)
++			sock->is_poll_in(sock);
++		if (pfd[i].revents & POLLOUT)
++			sock->is_poll_out(sock);
++		if (pfd[i].revents & POLLHUP)
++			sock->is_poll_hup(sock);
++	}
++
++	goto again;
++}
++
++isns_message_t *
++isns_recv_message(struct timeval *timeout)
++{
++	isns_message_t	*msg;
++	struct timeval	end;
++
++	if (timeout == NULL)
++		return __isns_recv_message(NULL, NULL);
++
++	gettimeofday(&end, NULL);
++	timeradd(&end, timeout, &end);
++	msg = __isns_recv_message(&end, NULL);
++
++	if (msg == NULL)
++		return msg;
++	isns_debug_socket("Next message xid=%04x\n", msg->im_xid);
++	if (msg->im_security) {
++		isns_debug_message("Received authenticated message from \"%s\"\n",
++				isns_principal_name(msg->im_security));
++	} else if (isns_config.ic_security) {
++		isns_debug_message("Received unauthenticated message\n");
++	} else {
++		isns_debug_message("Received message\n");
++	}
++	return msg;
++}
++
++int
++isns_socket_send(isns_socket_t *sock, isns_message_t *msg)
++{
++	struct isns_hdr	*hdr;
++	size_t		pdu_len;
++	buf_t		*bp;
++
++	/* If the socket is disconnected, and the
++	 * reconnect timeout is not set, force a
++	 * reconnect right away. */
++	if (sock->is_state == ISNS_SOCK_DISCONNECTED
++	 && !timerisset(&sock->is_deadline)) {
++		isns_net_set_timeout(sock,
++			isns_net_stream_reconnect, 0);
++	}
++
++	if (!(bp = msg->im_payload))
++		return 0;
++
++	pdu_len = buf_avail(bp);
++	if (pdu_len < sizeof(*hdr))
++		return 0;
++
++	/* Pad PDU to multiple of 4 bytes, if needed */
++	if (pdu_len & 3) {
++		unsigned int pad = 4 - (pdu_len & 3);
++
++		if (!buf_put(bp, "\0\0\0", pad))
++			return 0;
++		pdu_len += pad;
++	}
++
++	if (!(bp = buf_dup(bp)))
++		return 0;
++
++	hdr = buf_head(bp);
++
++	hdr->i_version = htons(msg->im_header.i_version);
++	hdr->i_function = htons(msg->im_header.i_function);
++	hdr->i_flags = htons(msg->im_header.i_flags);
++	hdr->i_length = htons(pdu_len - sizeof(*hdr));
++	hdr->i_xid = htons(msg->im_header.i_xid);
++	hdr->i_seq = htons(msg->im_header.i_seq);
++
++	/* For now, we deal with unfragmented messages only. */
++	hdr->i_flags |= htons(ISNS_F_FIRST_PDU|ISNS_F_LAST_PDU);
++
++	if (sock->is_security) {
++#ifdef WITH_SECURITY
++		hdr->i_flags |= htons(ISNS_F_AUTHBLK_PRESENT);
++		if (!isns_pdu_seal(sock->is_security, bp)) {
++			isns_debug_message("Error adding auth block to outgoing PDU\n");
++			goto error;
++		}
++#else
++		isns_debug_message("%s: Authentication not supported\n",
++				__FUNCTION__);
++		goto error;
++#endif
++	}
++
++	bp->addr = msg->im_addr;
++	bp->addrlen = msg->im_addrlen;
++
++	buf_list_append(&sock->is_xmit_buf, bp);
++	sock->is_poll_mask |= POLLOUT;
++
++	/* Set the retransmit timeout */
++	__set_timeout(&msg->im_resend_timeout, sock->is_retrans_timeout);
++	return 1;
++
++error:
++	buf_free(bp);
++	return 0;
++}
++
++/*
++ * Queue a message to a socket
++ */
++int
++isns_socket_queue_message(isns_socket_t *sock, isns_message_t *msg)
++{
++	if (!isns_socket_send(sock, msg))
++		return 0;
++
++	/* Insert sorted by timeout. For now, this amounts to
++	 * appending at the end of the list, but that may change
++	 * if we implement exponential backoff for UDP */
++	isns_message_queue_insert_sorted(&sock->is_pending,
++			ISNS_MQ_SORT_RESEND_TIMEOUT, msg);
++	msg->im_socket = sock;
++	return 1;
++}
++
++/*
++ * Retransmit any queued messages
++ */
++int
++isns_socket_retransmit_queued(isns_socket_t *sock)
++{
++	isns_message_t	*msg;
++	isns_list_t	*pos;
++
++	isns_debug_socket("%s(%p)\n", __FUNCTION__, sock);
++	isns_message_queue_foreach(&sock->is_pending, pos, msg) {
++		if (!isns_socket_send(sock, msg))
++			isns_warning("Unable to retransmit message\n");
++	}
++	return 1;
++}
++
++/*
++ * Submit a message to the socket, for asynchronous calls
++ */
++int
++isns_socket_submit(isns_socket_t *sock, isns_message_t *msg, long timeout)
++{
++	if (timeout <= 0)
++		timeout = isns_config.ic_network.call_timeout;
++
++	__set_timeout(&msg->im_timeout, timeout);
++	return isns_socket_queue_message(sock, msg);
++}
++
++/*
++ * Transmit a message and wait for a response.
++ */
++isns_message_t *
++isns_socket_call(isns_socket_t *sock, isns_message_t *msg, long timeout)
++{
++	isns_message_t	*resp;
++
++	debug_verbose("isns_socket_call(sock=%p, msg=%p, timeout=%ld)\n",
++			sock, msg, timeout);
++	if (timeout <= 0)
++		timeout = isns_config.ic_network.call_timeout;
++
++	__set_timeout(&msg->im_timeout, timeout);
++	if (!isns_socket_queue_message(sock, msg))
++		return NULL;
++
++	sock->is_report_failure = 1;
++	resp = __isns_recv_message(NULL, msg);
++	sock->is_report_failure = 0;
++
++	if (isns_message_unlink(msg)) {
++		/* We can get here if __isns_recv_message returned
++		 * due to a fatal socket error. */
++		isns_debug_socket("%s: msg not unlinked!\n", __FUNCTION__);
++		isns_message_release(msg);
++	}
++
++	if (resp == NULL && sock->is_type == SOCK_STREAM)
++		isns_net_close(sock, ISNS_SOCK_DISCONNECTED);
++
++	return resp;
++}
++
++/*
++ * Resolve a hostname
++ */
++struct addrinfo *
++isns_get_address_list(const char *addrspec, const char *port,
++		int af_hint, int sock_type, int flags)
++{
++	struct addrinfo hints, *found = NULL, *res = NULL;
++	char	*copy = NULL, *host = NULL, *s;
++	int	rv;
++
++	memset(&hints, 0, sizeof(hints));
++	hints.ai_flags = AI_ADDRCONFIG;
++
++	if (addrspec && addrspec[0] == '/') {
++		if (af_hint != AF_LOCAL && af_hint != AF_UNSPEC) {
++			isns_debug_socket("Path as address, but af_hint=%d\n",
++					af_hint);
++			goto bad_address;
++		}
++
++		res = make_addrinfo_unix(addrspec, SOCK_STREAM);
++		goto out;
++	}
++
++	if (addrspec) {
++		copy = host = isns_strdup(addrspec);
++		if (*host == '[') {
++			hints.ai_flags |= AI_NUMERICHOST;
++			if ((s = strchr(host, ']')) == NULL)
++				goto bad_address;
++
++			*s++ = '\0';
++			if (*s == ':')
++				port = ++s;
++			else if (*s)
++				goto bad_address;
++		} else if ((s = strchr(host, ':')) != NULL) {
++			*s++ = '\0';
++			if (!*s)
++				goto bad_address;
++			port = s;
++		}
++
++		if (*host == '\0')
++			host = NULL;
++	} else if (port == NULL) {
++		/* Just wildcard */
++		res = make_addrinfo_any(af_hint, sock_type);
++		goto out;
++	}
++
++	hints.ai_family = af_hint;
++	hints.ai_flags |= flags;
++	hints.ai_socktype = sock_type;
++	if (af_hint == AF_INET6)
++		hints.ai_flags |= AI_V4MAPPED;
++
++	rv = getaddrinfo(host, port, &hints, &found);
++	if (rv) {
++		isns_error("Cannot resolve address \"%s\": %s\n",
++			addrspec, gai_strerror(rv));
++		goto out;
++	}
++
++	if (found == NULL) {
++		isns_error("No useable addresses returned.\n");
++		goto out;
++	}
++
++	res = clone_addrinfo(found);
++
++out:
++	if (found)
++		freeaddrinfo(found);
++	isns_free(copy);
++	return res;
++
++bad_address:
++	isns_error("Cannot parse address spec \"%s\"\n",
++		addrspec);
++	goto out;
++}
++
++int
++isns_get_address(struct sockaddr_storage *result,
++			const char *addrspec,
++			const char *port,
++			int af_hint, int sock_type, int flags)
++{
++	struct addrinfo	*ai;
++	int alen;
++
++	if (!(ai = isns_get_address_list(addrspec, port, af_hint, sock_type, flags)))
++		return -1;
++
++	alen = ai->ai_addrlen;
++	if (alen > sizeof(*result))
++		return -1;
++	memcpy(result, ai->ai_addr, alen);
++	release_addrinfo(ai);
++	return alen;
++}
++
++/*
++ * Get the canonical hostname
++ */
++char *
++isns_get_canon_name(const char *hostname)
++{
++	struct addrinfo hints, *res = NULL;
++	char	*fqdn = NULL;
++	int	rv;
++
++	memset(&hints, 0, sizeof(hints));
++	hints.ai_flags = AI_CANONNAME;
++
++	rv = getaddrinfo(hostname, NULL, &hints, &res);
++	if (rv) {
++		isns_error("Cannot resolve hostname \"%s\": %s\n",
++			hostname, gai_strerror(rv));
++		goto out;
++	}
++
++	if (res == NULL) {
++		isns_error("No useable addresses returned.\n");
++		goto out;
++	}
++
++
++	fqdn = isns_strdup(res->ai_canonname);
++
++out:
++	if (res)
++		freeaddrinfo(res);
++	return fqdn;
++}
++
++int
++isns_socket_get_local_addr(const isns_socket_t *sock,
++		struct sockaddr_storage *addr)
++{
++	socklen_t	alen;
++
++	if (sock->is_desc < 0)
++		return 0;
++	if (getsockname(sock->is_desc,
++			(struct sockaddr *) addr, &alen) < 0) {
++		isns_error("getsockname: %m\n");
++		return 0;
++	}
++
++	return 1;
++}
++
++int
++isns_socket_get_portal_info(const isns_socket_t *sock,
++				isns_portal_info_t *portal)
++{
++	struct sockaddr_storage addr;
++	socklen_t	alen;
++	int		fd, success = 0;
++
++	memset(portal, 0, sizeof(*portal));
++
++	/* If the socket is currently closed (eg because the
++	 * server shut down the connection), we cannot get the
++	 * local address easily. Create a temporary UDP socket,
++	 * connect it, and query that socket. */
++	if ((fd = sock->is_desc) < 0) {
++		const struct sockaddr *daddr;
++		
++		daddr = (struct sockaddr *) &sock->is_dst.addr;
++		fd = socket(daddr->sa_family, SOCK_DGRAM, 0);
++		if (fd < 0)
++			goto out;
++		if (connect(fd, daddr, sizeof(sock->is_dst.addr)) < 0)
++			goto out;
++	}
++
++	alen = sizeof(addr);
++	if (getsockname(fd, (struct sockaddr *) &addr, &alen) < 0) {
++		isns_error("getsockname: %m\n");
++		goto out;
++	}
++
++	if (!isns_portal_from_sockaddr(portal, &addr))
++		goto out;
++	if (sock->is_type == SOCK_STREAM)
++		portal->proto = IPPROTO_TCP;
++	else
++		portal->proto = IPPROTO_UDP;
++
++	debug_verbose("socket_get_portal: %s\n", isns_portal_string(portal));
++	success = 1;
++
++out:
++	/* If we used a temp UDP socket, close it */
++	if (fd >= 0 && fd != sock->is_desc)
++		close(fd);
++	return success;
++}
++
++isns_socket_t *
++isns_socket_find_server(const isns_portal_info_t *portal)
++{
++	struct sockaddr_storage bound_addr;
++	int sock_type, addr_len;
++	isns_list_t *pos, *next;
++
++	addr_len = isns_portal_to_sockaddr(portal, &bound_addr);
++	if ((sock_type = isns_socket_type_from_portal(portal)) < 0)
++		return NULL;
++
++	isns_list_foreach(&all_sockets, pos, next) {
++		isns_socket_t	*sock = isns_list_item(isns_socket_t, is_list, pos);
++
++		if (!sock->is_client
++		 && sock->is_type == sock_type
++		 && sock->is_dst.addrlen == addr_len
++		 && !memcmp(&sock->is_dst.addr, &bound_addr, addr_len)) {
++			sock->is_users++;
++			return sock;
++		}
++	}
++
++	return NULL;
++}
++
++int
++isns_addr_get_port(const struct sockaddr *addr)
++{
++	const struct sockaddr_in *sin;
++	const struct sockaddr_in6 *six;
++
++	switch (addr->sa_family) {
++	case AF_INET:
++		sin = (const struct sockaddr_in *) addr;
++		return ntohs(sin->sin_port);
++
++	case AF_INET6:
++		six = (const struct sockaddr_in6 *) addr;
++		return ntohs(six->sin6_port);
++	}
++	return 0;
++}
++
++void
++isns_addr_set_port(struct sockaddr *addr, unsigned int port)
++{
++	struct sockaddr_in *sin;
++	struct sockaddr_in6 *six;
++
++	switch (addr->sa_family) {
++	case AF_INET:
++		sin = (struct sockaddr_in *) addr;
++		sin->sin_port = htons(port);
++		break;
++
++	case AF_INET6:
++		six = (struct sockaddr_in6 *) addr;
++		six->sin6_port = htons(port);
++		break;
++	}
++}
+diff --git a/utils/open-isns/socket.h b/utils/open-isns/socket.h
+new file mode 100644
+index 0000000..cc63d23
+--- /dev/null
++++ b/utils/open-isns/socket.h
+@@ -0,0 +1,95 @@
++/*
++ * iSNS network code
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_SOCKET_H
++#define ISNS_SOCKET_H
++
++#include "isns.h"
++#include "buffer.h"
++#include "message.h"
++
++struct isns_partial_msg {
++	isns_message_t		imp_base;
++	uint32_t		imp_flags;
++	uint32_t		imp_first_seq;
++	uint32_t		imp_last_seq;
++	unsigned int		imp_pdu_count;
++	unsigned int		imp_msg_size;
++	buf_t *			imp_chain;
++
++	struct ucred		imp_credbuf;
++};
++
++#define imp_users		imp_base.im_users
++#define imp_list		imp_base.im_list
++#define imp_xid			imp_base.im_xid
++#define imp_header		imp_base.im_header
++#define imp_addr		imp_base.im_addr
++#define imp_addrlen		imp_base.im_addrlen
++#define imp_header		imp_base.im_header
++#define imp_payload		imp_base.im_payload
++#define imp_security		imp_base.im_security
++#define imp_creds		imp_base.im_creds
++
++enum {
++	ISNS_SOCK_LISTENING,
++	ISNS_SOCK_CONNECTING,
++	ISNS_SOCK_IDLE,
++	ISNS_SOCK_FAILED,
++	ISNS_SOCK_DISCONNECTED,
++	ISNS_SOCK_DEAD,
++};
++
++/* Helper class */
++struct __isns_socket_addr {
++	struct sockaddr_storage	addr;
++	socklen_t		addrlen;
++	struct addrinfo *	list;
++};
++
++struct isns_socket {
++	isns_list_t		is_list;
++	int			is_desc;
++	int			is_type;
++	unsigned int		is_client : 1,
++				is_autoclose : 1,
++				is_disconnect_fatal : 1,
++				is_report_failure : 1,
++				is_destroy : 1;
++	unsigned int		is_users;
++	int			is_poll_mask;
++	int			is_state;
++
++	isns_security_t *	is_security;
++
++	struct __isns_socket_addr is_src, is_dst;
++
++	unsigned int		is_retrans_timeout;
++
++	/* If we're past this time, is_timeout() is called. */
++	struct timeval		is_deadline;
++
++	buf_t *			is_recv_buf;
++	buf_t *			is_xmit_buf;
++
++	size_t			is_queue_size;
++	isns_message_queue_t	is_partial;
++	isns_message_queue_t	is_complete;
++	isns_message_queue_t	is_pending;
++
++	void			(*is_poll_in)(isns_socket_t *);
++	void			(*is_poll_out)(isns_socket_t *);
++	void			(*is_poll_hup)(isns_socket_t *);
++	void			(*is_poll_err)(isns_socket_t *);
++	void			(*is_timeout)(isns_socket_t *);
++	void			(*is_error)(isns_socket_t *, int);
++};
++
++extern int			isns_socket_submit(isns_socket_t *,
++					isns_message_t *,
++					long);
++
++#endif /* ISNS_SOCKET_H */
+diff --git a/utils/open-isns/source.h b/utils/open-isns/source.h
+new file mode 100644
+index 0000000..59fb662
+--- /dev/null
++++ b/utils/open-isns/source.h
+@@ -0,0 +1,32 @@
++/*
++ * iSNS source attribute handling
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_SOURCE_H
++#define ISNS_SOURCE_H
++
++#include "attrs.h"
++
++struct isns_source {
++	unsigned int		is_users;
++	isns_attr_t *		is_attr;
++	unsigned int		is_untrusted : 1;
++
++	isns_object_t *		is_node;
++	unsigned int		is_node_type;
++
++	isns_object_t *		is_entity;
++};
++
++extern int		isns_source_encode(buf_t *, const isns_source_t *);
++extern int		isns_source_decode(buf_t *, isns_source_t **);
++extern int		isns_source_set_node(isns_source_t *, isns_db_t *);
++extern void		isns_source_set_entity(isns_source_t *, isns_object_t *);
++extern isns_source_t *	isns_source_dummy(void);
++
++extern char *		isns_build_source_pattern(const char *);
++extern int		isns_source_pattern_match(const char *, const char *);
++
++#endif /* ISNS_SOURCE_H */
+diff --git a/utils/open-isns/storage-node.c b/utils/open-isns/storage-node.c
+new file mode 100644
+index 0000000..97e54d1
+--- /dev/null
++++ b/utils/open-isns/storage-node.c
+@@ -0,0 +1,202 @@
++/*
++ * iSNS object model - storage node
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include "isns.h"
++#include "objects.h"
++#include "util.h"
++
++isns_object_t *
++isns_create_storage_node(const char *name, uint32_t type,
++			isns_object_t *parent)
++{
++	isns_object_t	*obj;
++
++	if (parent && !ISNS_IS_ENTITY(parent)) {
++		isns_warning("Invalid container type \"%s\" for storage node: "
++			"should be \"%s\"\n",
++			parent->ie_template->iot_name,
++			isns_entity_template.iot_name);
++		return NULL;
++	}
++
++	obj = isns_create_object(&isns_iscsi_node_template, NULL, parent);
++	isns_object_set_string(obj,
++			ISNS_TAG_ISCSI_NAME, name);
++	isns_object_set_uint32(obj,
++			ISNS_TAG_ISCSI_NODE_TYPE, type);
++
++	return obj;
++}
++
++isns_object_t *
++isns_create_storage_node2(const isns_source_t *source,
++				uint32_t type,
++				isns_object_t *parent)
++{
++	isns_attr_t	*name_attr;
++	isns_object_t	*obj;
++
++	if (parent && !ISNS_IS_ENTITY(parent)) {
++		isns_warning("Invalid container type \"%s\" for storage node: "
++			"should be \"%s\"\n",
++			parent->ie_template->iot_name,
++			isns_entity_template.iot_name);
++		return NULL;
++	}
++	if ((name_attr = isns_source_attr(source)) == NULL) {
++		isns_warning("No source attribute\n");
++		return NULL;
++	}
++
++	if (name_attr->ia_tag_id == ISNS_TAG_ISCSI_NAME) {
++		obj = isns_create_object(&isns_iscsi_node_template, NULL, parent);
++		isns_attr_list_update_attr(&obj->ie_attrs, name_attr);
++		isns_object_set_uint32(obj,
++				ISNS_TAG_ISCSI_NODE_TYPE, type);
++	} else {
++		/* No iFCP yet, sorry */
++		isns_warning("%s: source tag type %u not supported\n",
++				__FUNCTION__);
++		return NULL;
++	}
++
++	return obj;
++}
++
++isns_object_t *
++isns_create_iscsi_initiator(const char *name,
++			isns_object_t *parent)
++{
++	return isns_create_storage_node(name, 
++			1 << ISNS_ISCSI_NODE_TYPE_INITIATOR,
++			parent);
++}
++
++isns_object_t *
++isns_create_iscsi_target(const char *name,
++			isns_object_t *parent)
++{
++	return isns_create_storage_node(name, 
++			1 << ISNS_ISCSI_NODE_TYPE_TARGET,
++			parent);
++}
++
++const char *
++isns_storage_node_name(const isns_object_t *node)
++{
++	const isns_attr_t *attr;
++
++	if (node->ie_attrs.ial_count == 0)
++		return NULL;
++	attr = node->ie_attrs.ial_data[0];
++	if (attr->ia_value.iv_type != &isns_attr_type_string)
++		return NULL;
++
++	switch (attr->ia_tag_id) {
++	case ISNS_TAG_ISCSI_NAME:
++	case ISNS_TAG_FC_PORT_NAME_WWPN:
++		return attr->ia_value.iv_string;
++	}
++
++	return 0;
++
++}
++
++isns_attr_t *
++isns_storage_node_key_attr(const isns_object_t *node)
++{
++	if (node->ie_attrs.ial_count == 0)
++		return NULL;
++	return node->ie_attrs.ial_data[0];
++}
++
++static uint32_t iscsi_node_attrs[] = {
++	ISNS_TAG_ISCSI_NAME,
++	ISNS_TAG_ISCSI_NODE_TYPE,
++	ISNS_TAG_ISCSI_ALIAS,
++	ISNS_TAG_ISCSI_SCN_BITMAP,
++	ISNS_TAG_ISCSI_NODE_INDEX,
++	ISNS_TAG_WWNN_TOKEN,
++	ISNS_TAG_ISCSI_AUTHMETHOD,
++	/* RFC 4171 lists a "iSCSI node certificate"
++	 * as an option attribute of an iSCSI
++	 * storage node, but doesn't define it anywhere
++	 * in the spec.
++	 */
++};
++
++static uint32_t iscsi_node_key_attrs[] = {
++	ISNS_TAG_ISCSI_NAME,
++};
++
++isns_object_template_t		isns_iscsi_node_template = {
++	.iot_name	= "iSCSI Storage Node",
++	.iot_handle	= ISNS_OBJECT_TYPE_NODE,
++	.iot_attrs	= iscsi_node_attrs,
++	.iot_num_attrs	= array_num_elements(iscsi_node_attrs),
++	.iot_keys	= iscsi_node_key_attrs,
++	.iot_num_keys	= array_num_elements(iscsi_node_key_attrs),
++	.iot_index	= ISNS_TAG_ISCSI_NODE_INDEX,
++	.iot_next_index	= ISNS_TAG_ISCSI_NODE_NEXT_INDEX,
++	.iot_container	= &isns_entity_template,
++};
++
++static uint32_t fc_port_attrs[] = {
++	ISNS_TAG_FC_PORT_NAME_WWPN,
++	ISNS_TAG_PORT_ID,
++	ISNS_TAG_FC_PORT_TYPE,
++	ISNS_TAG_SYMBOLIC_PORT_NAME,
++	ISNS_TAG_FABRIC_PORT_NAME,
++	ISNS_TAG_HARD_ADDRESS,
++	ISNS_TAG_PORT_IP_ADDRESS,
++	ISNS_TAG_CLASS_OF_SERVICE,
++	ISNS_TAG_FC4_TYPES,
++	ISNS_TAG_FC4_DESCRIPTOR,
++	ISNS_TAG_FC4_FEATURES,
++	ISNS_TAG_IFCP_SCN_BITMAP,
++	ISNS_TAG_PORT_ROLE,
++	ISNS_TAG_PERMANENT_PORT_NAME,
++};
++
++static uint32_t fc_port_key_attrs[] = {
++	ISNS_TAG_FC_PORT_NAME_WWPN,
++};
++
++isns_object_template_t		isns_fc_port_template = {
++	.iot_name	= "iFCP Port",
++	.iot_handle	= ISNS_OBJECT_TYPE_FC_PORT,
++	.iot_attrs	= fc_port_attrs,
++	.iot_num_attrs	= array_num_elements(fc_port_attrs),
++	.iot_keys	= fc_port_key_attrs,
++	.iot_num_keys	= array_num_elements(fc_port_key_attrs),
++	.iot_container	= &isns_entity_template,
++};
++
++static uint32_t fc_node_attrs[] = {
++	ISNS_TAG_FC_NODE_NAME_WWNN,
++	ISNS_TAG_SYMBOLIC_NODE_NAME,
++	ISNS_TAG_NODE_IP_ADDRESS,
++	ISNS_TAG_NODE_IPA,
++	ISNS_TAG_PROXY_ISCSI_NAME,
++};
++
++static uint32_t fc_node_key_attrs[] = {
++	ISNS_TAG_FC_NODE_NAME_WWNN,
++};
++
++isns_object_template_t		isns_fc_node_template = {
++	.iot_name	= "iFCP Device Node",
++	.iot_handle	= ISNS_OBJECT_TYPE_FC_NODE,
++	.iot_attrs	= fc_node_attrs,
++	.iot_num_attrs	= array_num_elements(fc_node_attrs),
++	.iot_keys	= fc_node_key_attrs,
++	.iot_num_keys	= array_num_elements(fc_node_key_attrs),
++	.iot_container	= &isns_fc_port_template,
++};
++
+diff --git a/utils/open-isns/sysdep-unix.c b/utils/open-isns/sysdep-unix.c
+new file mode 100644
+index 0000000..d2a9532
+--- /dev/null
++++ b/utils/open-isns/sysdep-unix.c
+@@ -0,0 +1,186 @@
++/*
++ * System dependent stuff
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <net/if.h>
++#include <sys/ioctl.h>
++#include <string.h>
++#include <unistd.h>
++#include "isns.h"
++#include "util.h"
++
++int isns_get_nr_portals(void)
++{
++	char		buffer[8192], *end, *ptr;
++	struct ifconf	ifc;
++	unsigned int	nportals = 0;
++	int		fd = -1;
++
++	if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
++		isns_error("%s: no socket - %m\n", __FUNCTION__);
++		return 0;
++	}
++
++	ifc.ifc_buf = buffer;
++	ifc.ifc_len = sizeof(buffer);
++	if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
++		isns_error("ioctl(SIOCGIFCONF): %m\n");
++		goto out;
++	}
++
++	ptr = buffer;
++	end = buffer + ifc.ifc_len;
++	while (ptr < end) {
++		struct ifreq	ifr;
++		struct sockaddr_storage ifaddr;
++		int		ifflags;
++
++		memcpy(&ifr, ptr, sizeof(ifr));
++		ptr += sizeof(ifr);
++
++		/* Get the interface addr */
++		memcpy(&ifaddr, &ifr.ifr_addr, sizeof(ifr.ifr_addr));
++
++		if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
++			isns_error("ioctl(%s, SIOCGIFFLAGS): %m\n",
++					ifr.ifr_name);
++			continue;
++		}
++		ifflags = ifr.ifr_flags;
++
++		if ((ifflags & IFF_UP) == 0)
++			continue;
++		if ((ifflags & IFF_LOOPBACK) != 0)
++			continue;
++
++		if (ifaddr.ss_family == AF_INET6 || ifaddr.ss_family == AF_INET)
++			nportals++;
++	}
++
++out:
++	if (fd >= 0)
++		close(fd);
++	return nportals;
++}
++
++int
++isns_enumerate_portals(isns_portal_info_t *result, unsigned int max)
++{
++	char		buffer[8192], *end, *ptr;
++	struct ifconf	ifc;
++	unsigned int	nportals = 0;
++	int		fd = -1;
++
++	if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
++		isns_error("%s: no socket - %m\n", __FUNCTION__);
++		return 0;
++	}
++
++	ifc.ifc_buf = buffer;
++	ifc.ifc_len = sizeof(buffer);
++	if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
++		isns_error("ioctl(SIOCGIFCONF): %m\n");
++		goto out;
++	}
++
++	ptr = buffer;
++	end = buffer + ifc.ifc_len;
++	while (ptr < end) {
++		struct ifreq	ifr;
++		struct sockaddr_storage ifaddr;
++		isns_portal_info_t portal;
++		int		ifflags;
++
++		memcpy(&ifr, ptr, sizeof(ifr));
++		ptr += sizeof(ifr);
++
++		/* Get the interface addr */
++		memcpy(&ifaddr, &ifr.ifr_addr, sizeof(ifr.ifr_addr));
++
++		if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
++			isns_error("ioctl(%s, SIOCGIFFLAGS): %m\n",
++					ifr.ifr_name);
++			continue;
++		}
++		ifflags = ifr.ifr_flags;
++
++		if ((ifflags & IFF_UP) == 0)
++			continue;
++		if ((ifflags & IFF_LOOPBACK) != 0)
++			continue;
++
++		if (!isns_portal_from_sockaddr(&portal, &ifaddr))
++			continue;
++
++		isns_debug_socket("Got interface %u: %s %s\n",
++				nportals, ifr.ifr_name,
++				isns_portal_string(&portal));
++		if (nportals < max)
++			result[nportals++] = portal;
++	}
++
++out:
++	if (fd >= 0)
++		close(fd);
++	return nportals;
++}
++
++int
++isns_portal_from_sockaddr(isns_portal_info_t *portal,
++		const struct sockaddr_storage *addr)
++{
++	struct sockaddr_in6 *six;
++	struct sockaddr_in *sin;
++
++	memset(portal, 0, sizeof(*portal));
++
++	/* May have to convert AF_INET to AF_INET6 */
++	six = &portal->addr;
++	switch (addr->ss_family) {
++	case AF_INET6:
++		memcpy(six, addr, sizeof(*six));
++		break;
++
++	case AF_INET:
++		sin = (struct sockaddr_in *) addr;
++		six->sin6_family = AF_INET6;
++		six->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
++		six->sin6_port = sin->sin_port;
++		break;
++
++	default:
++		return 0;
++	}
++
++	return 1;
++}
++
++int
++isns_portal_to_sockaddr(const isns_portal_info_t *portal,
++		struct sockaddr_storage *addr)
++{
++	const struct sockaddr_in6 *six = &portal->addr;
++	struct sockaddr_in *sin;
++
++	/* Check if this is really a v4 address is disguise.
++	 * If so, explicitly use an AF_INET socket - the
++	 * stack may not support IPv6.
++	 */
++	if (IN6_IS_ADDR_V4MAPPED(&six->sin6_addr)
++	 || IN6_IS_ADDR_V4COMPAT(&six->sin6_addr)) {
++		sin = (struct sockaddr_in *) addr;
++
++		memset(sin, 0, sizeof(*sin));
++		sin->sin_family = AF_INET;
++		sin->sin_addr.s_addr = six->sin6_addr.s6_addr32[3];
++		sin->sin_port = six->sin6_port;
++
++		return sizeof(*sin);
++	}
++	
++	/* This is the genuine article */
++	memcpy(addr, six, sizeof(*six));
++	return sizeof(*six);
++}
+diff --git a/utils/open-isns/tags.c b/utils/open-isns/tags.c
+new file mode 100644
+index 0000000..7413cee
+--- /dev/null
++++ b/utils/open-isns/tags.c
+@@ -0,0 +1,740 @@
++/*
++ * Define all iSNS tags with their types, etc.
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <string.h>
++#include <stdlib.h>
++#include <time.h>
++#include "isns-proto.h"
++#include "vendor.h"
++#include "attrs.h"
++#include "security.h"
++#include "objects.h"
++#include "util.h"
++
++#define ISNS_MAX_BUILTIN_TAG	4096
++
++
++static void	print_bitfield(unsigned long, char **, char *, size_t);
++static int	parse_bitfield( char **, const char *, uint32_t *);
++static const char *help_bitfield(char **);
++
++#define DECLARE_VALIDATOR(name) \
++static int		isns_##name##_validate(const isns_value_t *, const isns_policy_t *);
++#define DECLARE_ACCESSORS(name) \
++static int		isns_##name##_parse(isns_value_t *, const char *buf); \
++static void		isns_##name##_print(const isns_value_t *, char *buf, size_t size); \
++static const char *	isns_##name##_help(void)
++#define USE_VALIDATOR(name) \
++	.it_validate = isns_##name##_validate
++#define USE_ACCESSORS(name) \
++	.it_parse = isns_##name##_parse, \
++	.it_print = isns_##name##_print, \
++	.it_help = isns_##name##_help
++
++DECLARE_VALIDATOR(entity_protocol);
++DECLARE_ACCESSORS(entity_protocol);
++DECLARE_ACCESSORS(tcpudp_port);
++DECLARE_VALIDATOR(iscsi_node_type);
++DECLARE_ACCESSORS(iscsi_node_type);
++DECLARE_ACCESSORS(timestamp);
++DECLARE_ACCESSORS(portal_secbitmap);
++DECLARE_ACCESSORS(scn_bitmap);
++DECLARE_ACCESSORS(dd_features);
++DECLARE_ACCESSORS(policy_object_type);
++DECLARE_ACCESSORS(policy_function);
++
++static const char *isns_authmethod_help(void);
++
++#define TAG(ID, name, type, args...) \
++[ISNS_TAG_##ID] = { \
++	.it_id		= ISNS_TAG_##ID,	\
++	.it_name	= name, \
++	.it_type	= &isns_attr_type_##type, \
++	args \
++}
++
++static isns_tag_type_t	isns_tags[ISNS_MAX_BUILTIN_TAG] = {
++TAG(DELIMITER,			"Delimiter",		nil),
++TAG(ENTITY_IDENTIFIER,		"Entity identifier",	string),
++TAG(ENTITY_PROTOCOL,		"Entity protocol",	uint32,
++				USE_VALIDATOR(entity_protocol),
++				USE_ACCESSORS(entity_protocol)),
++TAG(MGMT_IP_ADDRESS,		"Mgmt IP address",	ipaddr),
++TAG(TIMESTAMP,			"Timestamp",		uint64,
++				USE_ACCESSORS(timestamp),
++				.it_readonly = 1),
++TAG(PROTOCOL_VERSION_RANGE,	"Protocol version range", range16),
++TAG(REGISTRATION_PERIOD,	"Registration Period",	uint32),
++TAG(ENTITY_INDEX,		"Entity index",		uint32,
++				.it_readonly = 1),
++TAG(ENTITY_NEXT_INDEX,		"Entity next index",	uint32,
++				.it_readonly = 1),
++TAG(PORTAL_IP_ADDRESS,		"Portal IP address",	ipaddr),
++TAG(PORTAL_TCP_UDP_PORT,	"Portal TCP/UDP port",	uint32,
++				USE_ACCESSORS(tcpudp_port)),
++TAG(ESI_INTERVAL,		"ESI interval",		uint32),
++TAG(ESI_PORT,			"ESI port",		uint32,
++				USE_ACCESSORS(tcpudp_port)),
++TAG(PORTAL_SYMBOLIC_NAME,	"Portal name",		string),
++TAG(PORTAL_INDEX,		"Portal index",		uint32),
++TAG(SCN_PORT,			"SCN port",		uint32,
++				USE_ACCESSORS(tcpudp_port)),
++TAG(PORTAL_SECURITY_BITMAP,	"Portal security bitmap", uint32,
++				USE_ACCESSORS(portal_secbitmap)),
++TAG(PORTAL_NEXT_INDEX,		"Portal next index",	uint32,
++				.it_readonly = 1),
++
++TAG(ISCSI_NAME,			"iSCSI name",		string),
++TAG(ISCSI_NODE_TYPE,		"iSCSI node type",	uint32,
++				USE_VALIDATOR(iscsi_node_type),
++				USE_ACCESSORS(iscsi_node_type)),
++TAG(ISCSI_ALIAS,		"iSCSI alias",		string),
++TAG(ISCSI_SCN_BITMAP,		"iSCSI SCN bitmap",	uint32,
++				USE_ACCESSORS(scn_bitmap)),
++TAG(ISCSI_NODE_INDEX,		"iSCSI node index",	uint32,
++				.it_readonly = 1),
++TAG(WWNN_TOKEN,			"WWNN token",		uint64),
++TAG(ISCSI_NODE_NEXT_INDEX,	"iSCSI node next index",uint32,
++				.it_readonly = 1),
++TAG(ISCSI_AUTHMETHOD,		"iSCSI auth method",	string,
++				.it_help = isns_authmethod_help),
++
++TAG(PG_ISCSI_NAME,		"Portal group name",	string),
++TAG(PG_PORTAL_IP_ADDR,		"Portal group address",	ipaddr),
++TAG(PG_PORTAL_TCP_UDP_PORT,	"Portal group port",	uint32,
++				USE_ACCESSORS(tcpudp_port)),
++TAG(PG_TAG,			"Portal group tag",	uint32),
++TAG(PG_INDEX,			"Portal group index",	uint32,
++				.it_readonly = 1),
++TAG(PG_NEXT_INDEX,		"Portal group next index",uint32,
++				.it_readonly = 1),
++
++/* FC Port */
++TAG(FC_PORT_NAME_WWPN,		"FC port name WWPN",	uint64),
++TAG(PORT_ID,			"FC port ID",		uint32),
++TAG(FC_PORT_TYPE,		"FC port type",		uint32),
++TAG(SYMBOLIC_PORT_NAME,		"FC symbolic port name",string),
++TAG(FABRIC_PORT_NAME,		"FC fabric port name",	uint64),
++TAG(HARD_ADDRESS,		"FC hard",		uint32),
++TAG(PORT_IP_ADDRESS,		"FC Port IP address",	ipaddr),
++TAG(CLASS_OF_SERVICE,		"FC service class",	uint32),
++TAG(FC4_TYPES,			"FC4 types",		opaque),
++TAG(FC4_DESCRIPTOR,		"FC4 descriptor",	string),
++TAG(FC4_FEATURES,		"FC4 features",		opaque),
++TAG(IFCP_SCN_BITMAP,		"iFCP SCN bitmap",	uint32,
++				USE_ACCESSORS(scn_bitmap)),
++TAG(PORT_ROLE,			"FC port role",		uint32),
++TAG(PERMANENT_PORT_NAME,	"FC permanent port name",uint64),
++TAG(FC4_TYPE_CODE,		"FC4 type code",	uint32),
++
++/* FC Node */
++TAG(FC_NODE_NAME_WWNN,		"FC node name",		uint64),
++TAG(SYMBOLIC_NODE_NAME,		"FC symbolic node name",string),
++TAG(NODE_IP_ADDRESS,		"FC node IP address",	ipaddr),
++TAG(NODE_IPA,			"FC node IPA",		uint64),
++TAG(PROXY_ISCSI_NAME,		"FC node proxy iSCSI name",string),
++
++/* Other FC tags to go here */
++
++/* Discovery domain set */
++TAG(DD_SET_ID,			"DD set ID",		uint32),
++TAG(DD_SET_SYMBOLIC_NAME,	"DD set name",		string),
++TAG(DD_SET_STATUS,		"DD set status",	uint32),
++TAG(DD_SET_NEXT_ID,		"DD set next ID",	uint32,
++				.it_readonly = 1),
++
++/* Discovery domain */
++TAG(DD_ID,			"DD ID",		uint32),
++TAG(DD_SYMBOLIC_NAME,		"DD name",		string),
++TAG(DD_MEMBER_ISCSI_INDEX,	"DD member iSCSI index",uint32,
++				.it_multiple = 1),
++TAG(DD_MEMBER_ISCSI_NAME,	"DD member iSCSI name",	string,
++				.it_multiple = 1),
++TAG(DD_MEMBER_FC_PORT_NAME,	"DD member FC WWPN",	string,
++				.it_multiple = 1),
++TAG(DD_MEMBER_PORTAL_INDEX,	"DD member portal index",uint32,
++				.it_multiple = 1),
++TAG(DD_MEMBER_PORTAL_IP_ADDR,	"DD member portal addr",ipaddr,
++				.it_multiple = 1),
++TAG(DD_MEMBER_PORTAL_TCP_UDP_PORT,"DD member portal port",uint32,
++				USE_ACCESSORS(tcpudp_port),
++				.it_multiple = 1),
++TAG(DD_FEATURES,		"DD features",		uint32,
++				USE_ACCESSORS(dd_features)),
++TAG(DD_NEXT_ID,			"DD next ID",		uint32,
++				.it_readonly = 1),
++};
++
++/*
++ * End of RFC defined tags
++ */
++#undef TAG
++
++/*
++ * Open-iSNS vendor specific tags
++ */
++#define TAG(ID, name, type, args...) \
++{ \
++	.it_id		= OPENISNS_TAG_##ID,	\
++	.it_name	= name, \
++	.it_type	= &isns_attr_type_##type, \
++	args \
++}
++
++static isns_tag_type_t	isns_vendor_tags[] = {
++TAG(POLICY_SPI,		"Security Policy Index",	string),
++TAG(POLICY_KEY,		"DSA security key",		opaque),
++TAG(POLICY_ENTITY,	"Policy allowed entity name",	string),
++TAG(POLICY_OBJECT_TYPE,	"Policy allowed object types",	uint32,
++				USE_ACCESSORS(policy_object_type)),
++TAG(POLICY_NODE_NAME,	"Policy allowed node name",	string,
++				.it_multiple = 1),
++TAG(POLICY_NODE_TYPE,	"Policy allowed node type",	uint32,
++				USE_VALIDATOR(iscsi_node_type),
++				USE_ACCESSORS(iscsi_node_type)),
++TAG(POLICY_FUNCTIONS,	"Policy allowed functions",	uint32,
++				USE_ACCESSORS(policy_function)),
++TAG(POLICY_VISIBLE_DD,	"Visible Discovery Domain",	string,
++				.it_multiple = 1),
++TAG(POLICY_DEFAULT_DD,	"Default Discovery Domain",	string),
++
++{ 0 }
++};
++
++/*
++ * End of vendor-specific tags
++ */
++
++static isns_tag_type_t	isns_unknown_tag = {
++	.it_id			= 0xffff,
++	.it_name		= "unknown",
++	.it_type		= &isns_attr_type_opaque,
++};
++
++/*
++ * Map iSNS attribute tag to its data type
++ */
++const isns_tag_type_t *
++isns_tag_type_by_id(uint32_t id)
++{
++	isns_tag_type_t	*tag;
++
++	if (id < ISNS_MAX_BUILTIN_TAG) {
++		tag = &isns_tags[id];
++		if (tag->it_type == NULL) {
++			*tag = isns_unknown_tag;
++			tag->it_id = id;
++		}
++		return tag;
++	}
++
++	for (tag = isns_vendor_tags; tag->it_name; ++tag) {
++		if (tag->it_id == id)
++			return tag;
++	}
++
++	return &isns_unknown_tag;
++}
++
++/*
++ * Specific validators/pretty printers
++ */
++int
++isns_entity_protocol_validate(const isns_value_t *value, const isns_policy_t *policy)
++{
++	enum isns_entity_protocol protocol = value->iv_uint32;
++
++	switch (protocol) {
++	case ISNS_ENTITY_PROTOCOL_NONE:
++	case ISNS_ENTITY_PROTOCOL_ISCSI:
++	case ISNS_ENTITY_PROTOCOL_IFCP:
++		return 1;
++	}
++	return 0;
++}
++
++int
++isns_entity_protocol_parse(isns_value_t *value, const char *string)
++{
++	uint32_t	prot;
++
++	if (!strcasecmp(string, "none"))
++		prot = ISNS_ENTITY_PROTOCOL_NONE;
++	else if (!strcasecmp(string, "iscsi"))
++		prot = ISNS_ENTITY_PROTOCOL_ISCSI;
++	else if (!strcasecmp(string, "ifcp"))
++		prot = ISNS_ENTITY_PROTOCOL_IFCP;
++	else
++		return 0;
++	value->iv_uint32 = prot;
++	return 1;
++}
++
++void
++isns_entity_protocol_print(const isns_value_t *value, char *buf, size_t size)
++{
++	enum isns_entity_protocol protocol = value->iv_uint32;
++	const char *prot_name;
++
++	switch (protocol) {
++	case ISNS_ENTITY_PROTOCOL_NONE:
++		prot_name = "None";
++		break;
++
++	case ISNS_ENTITY_PROTOCOL_ISCSI:
++		prot_name = "iSCSI";
++		break;
++
++	case ISNS_ENTITY_PROTOCOL_IFCP:
++		prot_name = "iFCP";
++		break;
++
++	default:
++		prot_name = "Unknown";
++	}
++	snprintf(buf, size, "%s (%u)", prot_name, protocol);
++}
++
++const char *
++isns_entity_protocol_help(void)
++{
++	return "one of None, iSCSI, iFCP";
++}
++
++/*
++ * TCP/UDP port
++ */
++int
++isns_tcpudp_port_parse(isns_value_t *value, const char *string)
++{
++	uint32_t	num;
++	const char	*ep;
++
++	num = strtoul(string, (char **) &ep, 0);
++	if (ep && *ep) {
++		if (!strcasecmp(ep, "/udp"))
++			num |= ISNS_PORTAL_PORT_UDP_MASK;
++		else
++		if (!strcasecmp(ep, "/tcp"))
++			/* nothing */;
++		else {
++			isns_error("Cannot parse port spec \"%s\"\n",
++					string);
++			return 0;
++		}
++	}
++	value->iv_uint32 = num;
++	return 1;
++}
++
++void
++isns_tcpudp_port_print(const isns_value_t *value, char *buf, size_t size)
++{
++	uint32_t	portspec = value->iv_uint32, num;
++
++	if (portspec == 0) {
++		snprintf(buf, size, "[default]");
++	} else {
++		num = portspec & 0xffff;
++		if (portspec & ISNS_PORTAL_PORT_UDP_MASK) {
++			snprintf(buf, size, "%u/udp", num);
++		} else {
++			snprintf(buf, size, "%u/tcp", num);
++		}
++	}
++}
++
++const char *
++isns_tcpudp_port_help(void)
++{
++	return "<port>/tcp, <port>/udp, or <port> (defaults to TCP)";
++}
++
++int
++isns_timestamp_parse(isns_value_t *value, const char *string)
++{
++	isns_error("Timestamp parsing not implemented\n");
++	return 0;
++}
++
++void
++isns_timestamp_print(const isns_value_t *value, char *buf, size_t size)
++{
++	time_t	timestamp = value->iv_uint64;
++	char	*str, *s;
++
++	str = ctime(&timestamp);
++	if ((s = strchr(str, '\n')) != NULL)
++		*s = '\0';
++
++	snprintf(buf, size, "%s", str);
++}
++
++const char *
++isns_timestamp_help(void)
++{
++	return NULL;
++}
++
++/*
++ * Helper macros to implement the off-the-shelf bitfield
++ * accessors.
++ */
++#define IMPLEMENT_BITFIELD_ACCESSORS(name) \
++int isns_##name##_parse(isns_value_t *value, const char *string) \
++{								\
++	return parse_bitfield(name##_bit_names, string,		\
++			&value->iv_uint32);			\
++}								\
++								\
++void								\
++isns_##name##_print(const isns_value_t *value, char *buf, size_t size) \
++{								\
++	print_bitfield(value->iv_uint32, name##_bit_names,	\
++			buf, size);				\
++}								\
++								\
++const char *							\
++isns_##name##_help(void)					\
++{								\
++	return help_bitfield(name##_bit_names);			\
++}
++
++
++static char *	iscsi_node_type_bit_names[32] = {
++[ISNS_ISCSI_NODE_TYPE_TARGET] = "Target",
++[ISNS_ISCSI_NODE_TYPE_INITIATOR] = "Initiator",
++[ISNS_ISCSI_NODE_TYPE_CONTROL] = "Control",
++};
++
++int
++isns_iscsi_node_type_validate(const isns_value_t *value, const isns_policy_t *policy)
++{
++	uint32_t	bits = value->iv_uint32, permitted;
++
++	permitted = ISNS_ISCSI_INITIATOR_MASK |
++			ISNS_ISCSI_TARGET_MASK |
++			ISNS_ISCSI_CONTROL_MASK;
++	if (bits & ~permitted)
++		return 0;
++
++	if (policy && !isns_policy_validate_node_type(policy, bits))
++		return 0;
++
++	return 1;
++}
++
++IMPLEMENT_BITFIELD_ACCESSORS(iscsi_node_type);
++
++/*
++ * Portal Security Bitmap
++ */
++static char *	portal_secbitmap_bit_names[32] = {
++[ISNS_PORTAL_SEC_BITMAP_VALID] = "bitmap valid",
++[ISNS_PORTAL_SEC_IPSEC_ENABLED] = "ipsec enabled",
++[ISNS_PORTAL_SEC_MAIN_MODE_ENABLED] = "main mode enabled",
++[ISNS_PORTAL_SEC_AGGR_MODE_ENABLED] = "aggressive mode enabled",
++[ISNS_PORTAL_SEC_PFS_ENABLED] = "pfs enabled",
++[ISNS_PORTAL_SEC_TRANSPORT_MODE_PREFERRED] = "transport mode preferred",
++[ISNS_PORTAL_SEC_TUNNEL_MODE_PREFERRED] = "tunnel mode preferred",
++};
++
++IMPLEMENT_BITFIELD_ACCESSORS(portal_secbitmap);
++
++/*
++ * SCN bitmap
++ */
++static char *	scn_bitmap_bit_names[32] = {
++[ISNS_SCN_DD_MEMBER_ADDED] = "DD/DDS member added",
++[ISNS_SCN_DD_MEMBER_REMOVED] = "DD/DDS member removed",
++[ISNS_SCN_OBJECT_UPDATED] = "object updated",
++[ISNS_SCN_OBJECT_ADDED] = "object added",
++[ISNS_SCN_OBJECT_REMOVED] = "object removed",
++[ISNS_SCN_MANAGEMENT_REGISTRATION] = "management registration",
++[ISNS_SCN_TARGET_AND_SELF_ONLY] = "target and self information only",
++[ISNS_SCN_INITIATOR_AND_SELF_ONLY] = "initiator and self information only",
++};
++
++IMPLEMENT_BITFIELD_ACCESSORS(scn_bitmap);
++
++/*
++ * DD features bitmap
++ */
++static char *	dd_features_bit_names[32] = {
++[ISNS_DD_BOOT_LIST_ENABLED] = "Boot list enabled",
++};
++
++IMPLEMENT_BITFIELD_ACCESSORS(dd_features);
++
++/*
++ * Policy: list of allowed functions
++ */
++static char *	policy_function_bit_names[32] = {
++[ISNS_DEVICE_ATTRIBUTE_REGISTER]= "DevAttrReg",
++[ISNS_DEVICE_ATTRIBUTE_QUERY]	= "DevAttrQry",
++[ISNS_DEVICE_GET_NEXT]		= "DevGetNext",
++[ISNS_DEVICE_DEREGISTER]	= "DevDereg",
++[ISNS_SCN_REGISTER]		= "SCNReg",
++[ISNS_SCN_DEREGISTER]		= "SCNDereg",
++[ISNS_SCN_EVENT]		= "SCNEvent",
++[ISNS_STATE_CHANGE_NOTIFICATION]= "SCN",
++[ISNS_DD_REGISTER]		= "DDReg",
++[ISNS_DD_DEREGISTER]		= "DDDereg",
++[ISNS_DDS_REGISTER]		= "DDSReg",
++[ISNS_DDS_DEREGISTER]		= "DDSDereg",
++[ISNS_ENTITY_STATUS_INQUIRY]	= "ESI",
++[ISNS_HEARTBEAT]		= "Heartbeat",
++};
++
++IMPLEMENT_BITFIELD_ACCESSORS(policy_function);
++
++/*
++ * Policy: list of allowed node types
++ */
++static char *	policy_object_type_bit_names[32] = {
++[ISNS_OBJECT_TYPE_ENTITY]	= "entity",
++[ISNS_OBJECT_TYPE_NODE]		= "iscsi-node",
++[ISNS_OBJECT_TYPE_PORTAL]	= "portal",
++[ISNS_OBJECT_TYPE_PG]		= "portal-group",
++[ISNS_OBJECT_TYPE_DD]		= "dd",
++[ISNS_OBJECT_TYPE_DDSET]	= "ddset",
++[ISNS_OBJECT_TYPE_POLICY]	= "policy",
++};
++
++static int
++isns_policy_object_type_parse(isns_value_t *vp, const char *buf)
++{
++	char	*copy, *s, *next;
++	int	rv = 0;
++
++	if (!strcasecmp(buf, "ALL")) {
++		vp->iv_uint32 = ~0;
++		return 1;
++	}
++	if (!strcasecmp(buf, "DEFAULT")) {
++		vp->iv_uint32 = ISNS_DEFAULT_OBJECT_ACCESS;
++		return 1;
++	}
++
++	vp->iv_uint32 = 0;
++	copy = isns_strdup(buf);
++	for (s = copy; s; s = next) {
++		char	*perm;
++		int	bit, mask = 0;
++
++		while (1) {
++			unsigned int n;
++
++			n = strcspn(s, ",+;|");
++			if (n) {
++				next = s + n;
++				if (*next)
++					*next++ = '\0';
++				break;
++			}
++			++n;
++		}
++
++		mask = ISNS_PERMISSION_READ;
++		if ((perm = strchr(s, ':')) != NULL) {
++			*perm++ = '\0';
++			mask = 0;
++			while (*perm) {
++				switch (*perm++) {
++				case 'R': case 'r':
++					mask = ISNS_PERMISSION_READ;
++					break;
++				case 'W': case 'w':
++					mask = ISNS_PERMISSION_READ;
++					break;
++				default:
++					goto failed;
++				}
++			}
++		}
++
++		for (bit = 0; bit < 32; ++bit) {
++			if (policy_object_type_bit_names[bit]
++			 && !strcasecmp(policy_object_type_bit_names[bit], s))
++				goto found;
++		}
++		goto failed;
++
++found:		vp->iv_uint32 |= ISNS_ACCESS(bit, mask);
++	}
++	rv = 1;
++
++failed:
++	isns_free(copy);
++	return rv;
++}
++
++static void
++isns_policy_object_type_print(const isns_value_t *vp, char *buf, size_t size)
++{
++	unsigned int	i, pos = 0;
++	uint32_t	mask;
++	const char	*sepa = "";
++
++	mask = vp->iv_uint32;
++	if (mask == 0) {
++		snprintf(buf, size, "<empty>");
++		return;
++	}
++
++	for (i = 0; i < 32; ++i, mask >>= 2) {
++		const char	*name;
++
++		if (!(mask & 3))
++			continue;
++
++		name = policy_object_type_bit_names[i];
++		if (name)
++			snprintf(buf + pos, size - pos, "%s%s:%s%s", sepa, name,
++				(mask & ISNS_PERMISSION_READ)? "r" : "",
++				(mask & ISNS_PERMISSION_WRITE)? "w" : "");
++		else
++			snprintf(buf + pos, size - pos, "%sbit%u:%s%s",sepa,  i,
++				(mask & ISNS_PERMISSION_READ)? "r" : "",
++				(mask & ISNS_PERMISSION_WRITE)? "w" : "");
++		sepa = ", ";
++		pos = strlen(buf);
++	}
++}
++
++static const char *
++isns_policy_object_type_help(void)
++{
++	static char	buffer[256];
++	unsigned int	i, n;
++	char		*sepa = "";
++
++	strcpy(buffer, "bitfield (type:perm): perm=R, W, or RW; type=");
++	n = strlen(buffer);
++
++	for (i = 0; i < 32; ++i) {
++		if (policy_object_type_bit_names[i]) {
++			snprintf(buffer + n, sizeof(buffer) - n,
++					"%s%s", sepa,
++					policy_object_type_bit_names[i]);
++			sepa = ", ";
++		}
++	}
++	return buffer;
++}
++
++/*
++ * Help message for AuthMethod
++ */
++const char *
++isns_authmethod_help(void)
++{
++	return "comma separated list, including of KRB5, SPKM1, SPKM2, SRP, CHAP, none";
++}
++
++/*
++ * Helper functions to deal with bitfields
++ */
++static void
++print_bitfield(unsigned long value, char **bit_names,
++		char *buf, size_t size)
++{
++	unsigned int bit, mask;
++	const char *sepa = "";
++	char *buf_end;
++
++	if (value == 0) {
++		snprintf(buf, size, "<NIL>");
++		return;
++	}
++
++	buf_end = buf + size;
++	for (bit = 0, mask = 1; mask; ++bit, mask <<= 1) {
++		char namebuf[16], *name;
++
++		if (!(value & mask))
++			continue;
++
++		if ((name = bit_names[bit]) == NULL) {
++			sprintf(namebuf, "bit%u", bit);
++			name = namebuf;
++		}
++
++		snprintf(buf, buf_end - buf, "%s%s", sepa, name);
++		buf += strlen(buf);
++		sepa = ", ";
++	}
++}
++
++static int
++parse_bitfield(char **bit_names,
++		const char *string,
++		uint32_t *result)
++{
++	*result = 0;
++
++	if (!strcasecmp(string, "ALL")) {
++		unsigned int	bit;
++
++		for (bit = 0; bit < 32; ++bit) {
++			if (bit_names[bit])
++				*result |= 1 << bit;
++		}
++		return 1;
++	}
++
++	if (!strcasecmp(string, "NONE"))
++		return 1;
++
++	while (*string) {
++		unsigned int	n, bit, match = 0;
++
++		n = strcspn(string, ",+;|");
++		if (n == 0)
++			goto next;
++
++		for (bit = 0; bit < 32; ++bit) {
++			if (!bit_names[bit])
++				continue;
++			if (!strncasecmp(bit_names[bit], string, n)) {
++				*result |= 1 << bit;
++				match++;
++			}
++		}
++		if (!match)
++			return 0;
++
++next:
++		string += n;
++		string += strspn(string, ",+;|");
++	}
++
++	return 1;
++}
++
++static const char *
++help_bitfield(char **bit_names)
++{
++	static char	buffer[1024];
++	char		*pos, sepa = ':';
++	unsigned int	bit;
++
++	strcpy(buffer, "bitfield");
++	pos = strchr(buffer, '\0');
++
++	for (bit = 0; bit < 32; ++bit) {
++		if (bit_names[bit] == NULL)
++			continue;
++
++		snprintf(pos, sizeof(buffer) - (pos - buffer),
++				"%c %s", sepa, bit_names[bit]);
++
++		pos += strlen(pos);
++		sepa = ',';
++	}
++	return buffer;
++}
++
+diff --git a/utils/open-isns/tests/.cvsignore b/utils/open-isns/tests/.cvsignore
+new file mode 100644
+index 0000000..fa1eb3c
+--- /dev/null
++++ b/utils/open-isns/tests/.cvsignore
+@@ -0,0 +1,2 @@
++*.swp
++pauw[1-9]
+diff --git a/utils/open-isns/tests/Makefile b/utils/open-isns/tests/Makefile
+new file mode 100644
+index 0000000..778195a
+--- /dev/null
++++ b/utils/open-isns/tests/Makefile
+@@ -0,0 +1,40 @@
++#
++# Simple makefile to run regression tests, and to
++# document how to run them.
++
++#
++# Each test case is a perl script, testXX.pl. Run as
++# 	perl testXX.pl
++# Optionally followed by
++#  -q	quiet - just print a header line, and the overall result
++#  -v	verbose - display more detailed information, including the
++#	commands being run
++#  -f	fast - skip tests that take more than 15 seconds
++#
++# The default is to be slightly verbose, and display a comment
++# about each stage of the test.
++
++# All test related data is kept in /tmp/isns-test, with a
++# subdirectory for each test.
++# For instance, test01 will create
++#  /tmp/isns-test/test01/server0
++#  /tmp/isns-test/test01/client1
++#  /tmp/isns-test/test01/dump
++#
++# The server and client directories will contain configuration
++# data, logfiles, and (for the server) the Unix socket, the
++# PID file, and the database.
++#
++# The dump directory contains snapshots of the on-disk database
++# for each test stage (if the test stage involves a verification
++# of the database).
++
++tests:
++	@for test in test*.pl; do \
++		perl $$test -q; \
++	done
++
++quick:
++	@for test in test*.pl; do \
++		perl $$test -q --fast; \
++	done
+diff --git a/utils/open-isns/tests/client.conf b/utils/open-isns/tests/client.conf
+new file mode 100644
+index 0000000..034a739
+--- /dev/null
++++ b/utils/open-isns/tests/client.conf
+@@ -0,0 +1,8 @@
++SourceName	= @NOT_SET@
++AuthName	= @NOT_SET@
++ServerAddress	= @NOT_SET@
++BindAddress	= @NOT_SET@
++Security	= @NOT_SET@
++AuthKeyFile	= @NOT_SET@
++ServerKeyFile	= @NOT_SET@
++ControlSocket	= @NOT_SET@
+diff --git a/utils/open-isns/tests/data/test01/01-enroll b/utils/open-isns/tests/data/test01/01-enroll
+new file mode 100644
+index 0000000..f59329b
+--- /dev/null
++++ b/utils/open-isns/tests/data/test01/01-enroll
+@@ -0,0 +1,18 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test01/server0/database
++Last EID:    1
++Last Index:  4
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test01/02-registration b/utils/open-isns/tests/data/test01/02-registration
+new file mode 100644
+index 0000000..fd26f3c
+--- /dev/null
++++ b/utils/open-isns/tests/data/test01/02-registration
+@@ -0,0 +1,42 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test01/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 12:40:58 2007
++  0007  uint32      : Entity index = 4
++--------------
++Object:      index=5 type=<iSCSI Storage Node> state=mature parent=4
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 5
++--------------
++Object:      index=6 type=<Portal> state=mature parent=4
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 6
++--------------
++Object:      index=7 type=<iSCSI Portal Group> state=mature parent=4
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 7
+diff --git a/utils/open-isns/tests/data/test01/03-query b/utils/open-isns/tests/data/test01/03-query
+new file mode 100644
+index 0000000..8208f83
+--- /dev/null
++++ b/utils/open-isns/tests/data/test01/03-query
+@@ -0,0 +1,20 @@
++object[0] = <Network Entity>
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 12:41:41 2007
++  0007  uint32      : Entity index = 4
++object[1] = <iSCSI Storage Node>
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 5
++object[2] = <Portal>
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 6
++object[3] = <iSCSI Portal Group>
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 7
+diff --git a/utils/open-isns/tests/data/test01/03-registration b/utils/open-isns/tests/data/test01/03-registration
+new file mode 100644
+index 0000000..affd69a
+--- /dev/null
++++ b/utils/open-isns/tests/data/test01/03-registration
+@@ -0,0 +1,20 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test01/server0/database
++Last EID:    1
++Last Index:  4
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "isns.client1"
++  0602v string      : Policy allowed source name = "isns.client1"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "isns.client2"
++  0602v string      : Policy allowed source name = "isns.client2"
++  0607v string      : Policy allowed node name = "isns.client2"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+diff --git a/utils/open-isns/tests/data/test01/99-unregistration b/utils/open-isns/tests/data/test01/99-unregistration
+new file mode 100644
+index 0000000..c7518ff
+--- /dev/null
++++ b/utils/open-isns/tests/data/test01/99-unregistration
+@@ -0,0 +1,18 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test01/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test02/01-enroll b/utils/open-isns/tests/data/test02/01-enroll
+new file mode 100644
+index 0000000..e91fa0b
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/01-enroll
+@@ -0,0 +1,18 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test02/server0/database
++Last EID:    1
++Last Index:  4
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test02/02-enroll b/utils/open-isns/tests/data/test02/02-enroll
+new file mode 100644
+index 0000000..dbcb735
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/02-enroll
+@@ -0,0 +1,24 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test02/server0/database
++Last EID:    1
++Last Index:  5
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client2.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client2"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++  0608v uint32      : Policy allowed node type = Target
+diff --git a/utils/open-isns/tests/data/test02/03-registration b/utils/open-isns/tests/data/test02/03-registration
+new file mode 100644
+index 0000000..ec607e6
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/03-registration
+@@ -0,0 +1,72 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test02/server0/database
++Last EID:    1
++Last Index:  13
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client2.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client2"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++  0608v uint32      : Policy allowed node type = Target
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:53 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 6
++--------------
++Object:      index=7 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 127.1.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 7
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.1.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
++--------------
++Object:      index=9 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client2.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:53 2007
++  0007  uint32      : Entity index = 9
++--------------
++Object:      index=10 type=<iSCSI Storage Node> state=mature parent=9
++  0020  string      : iSCSI name = "isns.client2"
++  0021  uint32      : iSCSI node type = Target
++  0024  uint32      : iSCSI node index = 10
++--------------
++Object:      index=11 type=<Portal> state=mature parent=9
++  0010  ipaddr      : Portal IP address = 127.1.0.2
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 11
++--------------
++Object:      index=12 type=<iSCSI Portal Group> state=mature parent=9
++  0030  string      : Portal group name = "isns.client2"
++  0031  ipaddr      : Portal group address = 127.1.0.2
++  0032  uint32      : Portal group port = 3260/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
+diff --git a/utils/open-isns/tests/data/test02/04-query b/utils/open-isns/tests/data/test02/04-query
+new file mode 100644
+index 0000000..fbdb0c0
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/04-query
+@@ -0,0 +1,20 @@
++object[0] = <Network Entity>
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:54 2007
++  0007  uint32      : Entity index = 5
++object[1] = <iSCSI Storage Node>
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 6
++object[2] = <Portal>
++  0010  ipaddr      : Portal IP address = 127.1.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 7
++object[3] = <iSCSI Portal Group>
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.1.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
+diff --git a/utils/open-isns/tests/data/test02/05-query b/utils/open-isns/tests/data/test02/05-query
+new file mode 100644
+index 0000000..a35db9e
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/05-query
+@@ -0,0 +1,20 @@
++object[0] = <Network Entity>
++  0001  string      : Entity identifier = "client2.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:54 2007
++  0007  uint32      : Entity index = 9
++object[1] = <iSCSI Storage Node>
++  0020  string      : iSCSI name = "isns.client2"
++  0021  uint32      : iSCSI node type = Target
++  0024  uint32      : iSCSI node index = 10
++object[2] = <Portal>
++  0010  ipaddr      : Portal IP address = 127.1.0.2
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 11
++object[3] = <iSCSI Portal Group>
++  0030  string      : Portal group name = "isns.client2"
++  0031  ipaddr      : Portal group address = 127.1.0.2
++  0032  uint32      : Portal group port = 3260/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
+diff --git a/utils/open-isns/tests/data/test02/06-dd-registration b/utils/open-isns/tests/data/test02/06-dd-registration
+new file mode 100644
+index 0000000..833f62a
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/06-dd-registration
+@@ -0,0 +1,81 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test02/server0/database
++Last EID:    1
++Last Index:  14
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client2.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client2"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++  0608v uint32      : Policy allowed node type = Target
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:54 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 6
++--------------
++Object:      index=7 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 127.1.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 7
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.1.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
++--------------
++Object:      index=9 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client2.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:54 2007
++  0007  uint32      : Entity index = 9
++--------------
++Object:      index=10 type=<iSCSI Storage Node> state=mature parent=9
++  0020  string      : iSCSI name = "isns.client2"
++  0021  uint32      : iSCSI node type = Target
++  0024  uint32      : iSCSI node index = 10
++--------------
++Object:      index=11 type=<Portal> state=mature parent=9
++  0010  ipaddr      : Portal IP address = 127.1.0.2
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 11
++--------------
++Object:      index=12 type=<iSCSI Portal Group> state=mature parent=9
++  0030  string      : Portal group name = "isns.client2"
++  0031  ipaddr      : Portal group address = 127.1.0.2
++  0032  uint32      : Portal group port = 3260/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
++--------------
++Object:      index=13 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 6
++  0814  string      : DD member iSCSI name = "isns.client1"
++  0813  uint32      : DD member iSCSI index = 10
++  0814  string      : DD member iSCSI name = "isns.client2"
+diff --git a/utils/open-isns/tests/data/test02/07-query b/utils/open-isns/tests/data/test02/07-query
+new file mode 100644
+index 0000000..de13226
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/07-query
+@@ -0,0 +1,40 @@
++object[0] = <Network Entity>
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:54 2007
++  0007  uint32      : Entity index = 5
++object[1] = <iSCSI Storage Node>
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 6
++object[2] = <Portal>
++  0010  ipaddr      : Portal IP address = 127.1.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 7
++object[3] = <iSCSI Portal Group>
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.1.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
++object[4] = <Network Entity>
++  0001  string      : Entity identifier = "client2.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:54 2007
++  0007  uint32      : Entity index = 9
++object[5] = <iSCSI Storage Node>
++  0020  string      : iSCSI name = "isns.client2"
++  0021  uint32      : iSCSI node type = Target
++  0024  uint32      : iSCSI node index = 10
++object[6] = <Portal>
++  0010  ipaddr      : Portal IP address = 127.1.0.2
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 11
++object[7] = <iSCSI Portal Group>
++  0030  string      : Portal group name = "isns.client2"
++  0031  ipaddr      : Portal group address = 127.1.0.2
++  0032  uint32      : Portal group port = 3260/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
+diff --git a/utils/open-isns/tests/data/test02/08-query b/utils/open-isns/tests/data/test02/08-query
+new file mode 100644
+index 0000000..de13226
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/08-query
+@@ -0,0 +1,40 @@
++object[0] = <Network Entity>
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:54 2007
++  0007  uint32      : Entity index = 5
++object[1] = <iSCSI Storage Node>
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 6
++object[2] = <Portal>
++  0010  ipaddr      : Portal IP address = 127.1.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 7
++object[3] = <iSCSI Portal Group>
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.1.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
++object[4] = <Network Entity>
++  0001  string      : Entity identifier = "client2.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:54 2007
++  0007  uint32      : Entity index = 9
++object[5] = <iSCSI Storage Node>
++  0020  string      : iSCSI name = "isns.client2"
++  0021  uint32      : iSCSI node type = Target
++  0024  uint32      : iSCSI node index = 10
++object[6] = <Portal>
++  0010  ipaddr      : Portal IP address = 127.1.0.2
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 11
++object[7] = <iSCSI Portal Group>
++  0030  string      : Portal group name = "isns.client2"
++  0031  ipaddr      : Portal group address = 127.1.0.2
++  0032  uint32      : Portal group port = 3260/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
+diff --git a/utils/open-isns/tests/data/test02/09-query b/utils/open-isns/tests/data/test02/09-query
+new file mode 100644
+index 0000000..a35db9e
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/09-query
+@@ -0,0 +1,20 @@
++object[0] = <Network Entity>
++  0001  string      : Entity identifier = "client2.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:54 2007
++  0007  uint32      : Entity index = 9
++object[1] = <iSCSI Storage Node>
++  0020  string      : iSCSI name = "isns.client2"
++  0021  uint32      : iSCSI node type = Target
++  0024  uint32      : iSCSI node index = 10
++object[2] = <Portal>
++  0010  ipaddr      : Portal IP address = 127.1.0.2
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 11
++object[3] = <iSCSI Portal Group>
++  0030  string      : Portal group name = "isns.client2"
++  0031  ipaddr      : Portal group address = 127.1.0.2
++  0032  uint32      : Portal group port = 3260/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
+diff --git a/utils/open-isns/tests/data/test02/10-dd-registration b/utils/open-isns/tests/data/test02/10-dd-registration
+new file mode 100644
+index 0000000..69bf9f6
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/10-dd-registration
+@@ -0,0 +1,87 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test02/server0/database
++Last EID:    1
++Last Index:  15
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client2.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client2"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++  0608v uint32      : Policy allowed node type = Target
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:54 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 6
++--------------
++Object:      index=7 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 127.1.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 7
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.1.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
++--------------
++Object:      index=9 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client2.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:18:54 2007
++  0007  uint32      : Entity index = 9
++--------------
++Object:      index=10 type=<iSCSI Storage Node> state=mature parent=9
++  0020  string      : iSCSI name = "isns.client2"
++  0021  uint32      : iSCSI node type = Target
++  0024  uint32      : iSCSI node index = 10
++--------------
++Object:      index=11 type=<Portal> state=mature parent=9
++  0010  ipaddr      : Portal IP address = 127.1.0.2
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 11
++--------------
++Object:      index=12 type=<iSCSI Portal Group> state=mature parent=9
++  0030  string      : Portal group name = "isns.client2"
++  0031  ipaddr      : Portal group address = 127.1.0.2
++  0032  uint32      : Portal group port = 3260/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
++--------------
++Object:      index=13 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 6
++  0814  string      : DD member iSCSI name = "isns.client1"
++  0813  uint32      : DD member iSCSI index = 10
++  0814  string      : DD member iSCSI name = "isns.client2"
++  0813  uint32      : DD member iSCSI index = 14
++  0814  string      : DD member iSCSI name = "iqn.com.foobar:disk1"
++--------------
++Object:      index=14 type=<iSCSI Storage Node> state=limbo
++  0020  string      : iSCSI name = "iqn.com.foobar:disk1"
++  0024  uint32      : iSCSI node index = 14
+diff --git a/utils/open-isns/tests/data/test02/11-query b/utils/open-isns/tests/data/test02/11-query
+new file mode 100644
+index 0000000..5b4c49d
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/11-query
+@@ -0,0 +1,10 @@
++object[0] = <Discovery Domain>
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 6
++  0814  string      : DD member iSCSI name = "isns.client1"
++  0813  uint32      : DD member iSCSI index = 10
++  0814  string      : DD member iSCSI name = "isns.client2"
++  0813  uint32      : DD member iSCSI index = 14
++  0814  string      : DD member iSCSI name = "iqn.com.foobar:disk1"
+diff --git a/utils/open-isns/tests/data/test02/12-dd-deregistration b/utils/open-isns/tests/data/test02/12-dd-deregistration
+new file mode 100644
+index 0000000..d330b2a
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/12-dd-deregistration
+@@ -0,0 +1,85 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test02/server0/database
++Last EID:    1
++Last Index:  15
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client2.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client2"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++  0608v uint32      : Policy allowed node type = Target
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:20:33 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 6
++--------------
++Object:      index=7 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 127.1.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 7
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.1.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
++--------------
++Object:      index=9 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client2.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:20:33 2007
++  0007  uint32      : Entity index = 9
++--------------
++Object:      index=10 type=<iSCSI Storage Node> state=mature parent=9
++  0020  string      : iSCSI name = "isns.client2"
++  0021  uint32      : iSCSI node type = Target
++  0024  uint32      : iSCSI node index = 10
++--------------
++Object:      index=11 type=<Portal> state=mature parent=9
++  0010  ipaddr      : Portal IP address = 127.1.0.2
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 11
++--------------
++Object:      index=12 type=<iSCSI Portal Group> state=mature parent=9
++  0030  string      : Portal group name = "isns.client2"
++  0031  ipaddr      : Portal group address = 127.1.0.2
++  0032  uint32      : Portal group port = 3260/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
++--------------
++Object:      index=13 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 6
++  0814  string      : DD member iSCSI name = "isns.client1"
++  0813  uint32      : DD member iSCSI index = 14
++  0814  string      : DD member iSCSI name = "iqn.com.foobar:disk1"
++--------------
++Object:      index=14 type=<iSCSI Storage Node> state=limbo
++  0020  string      : iSCSI name = "iqn.com.foobar:disk1"
++  0024  uint32      : iSCSI node index = 14
+diff --git a/utils/open-isns/tests/data/test02/13-dd-deregistration b/utils/open-isns/tests/data/test02/13-dd-deregistration
+new file mode 100644
+index 0000000..4175e8e
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/13-dd-deregistration
+@@ -0,0 +1,83 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test02/server0/database
++Last EID:    1
++Last Index:  15
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client2.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client2"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++  0608v uint32      : Policy allowed node type = Target
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:24:04 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 6
++--------------
++Object:      index=7 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 127.1.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 7
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.1.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
++--------------
++Object:      index=9 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client2.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:24:04 2007
++  0007  uint32      : Entity index = 9
++--------------
++Object:      index=10 type=<iSCSI Storage Node> state=mature parent=9
++  0020  string      : iSCSI name = "isns.client2"
++  0021  uint32      : iSCSI node type = Target
++  0024  uint32      : iSCSI node index = 10
++--------------
++Object:      index=11 type=<Portal> state=mature parent=9
++  0010  ipaddr      : Portal IP address = 127.1.0.2
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 11
++--------------
++Object:      index=12 type=<iSCSI Portal Group> state=mature parent=9
++  0030  string      : Portal group name = "isns.client2"
++  0031  ipaddr      : Portal group address = 127.1.0.2
++  0032  uint32      : Portal group port = 3260/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
++--------------
++Object:      index=13 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 6
++  0814  string      : DD member iSCSI name = "isns.client1"
++--------------
++Object:      index=14 type=<iSCSI Storage Node> state=limbo
++  0020  string      : iSCSI name = "iqn.com.foobar:disk1"
++  0024  uint32      : iSCSI node index = 14
+diff --git a/utils/open-isns/tests/data/test02/14-dd-registration b/utils/open-isns/tests/data/test02/14-dd-registration
+new file mode 100644
+index 0000000..56cfac3
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/14-dd-registration
+@@ -0,0 +1,85 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test02/server0/database
++Last EID:    1
++Last Index:  15
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client2.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client2"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++  0608v uint32      : Policy allowed node type = Target
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:24:04 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 6
++--------------
++Object:      index=7 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 127.1.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 7
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.1.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
++--------------
++Object:      index=9 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client2.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:24:04 2007
++  0007  uint32      : Entity index = 9
++--------------
++Object:      index=10 type=<iSCSI Storage Node> state=mature parent=9
++  0020  string      : iSCSI name = "isns.client2"
++  0021  uint32      : iSCSI node type = Target
++  0024  uint32      : iSCSI node index = 10
++--------------
++Object:      index=11 type=<Portal> state=mature parent=9
++  0010  ipaddr      : Portal IP address = 127.1.0.2
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 11
++--------------
++Object:      index=12 type=<iSCSI Portal Group> state=mature parent=9
++  0030  string      : Portal group name = "isns.client2"
++  0031  ipaddr      : Portal group address = 127.1.0.2
++  0032  uint32      : Portal group port = 3260/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
++--------------
++Object:      index=13 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 6
++  0814  string      : DD member iSCSI name = "isns.client1"
++  0813  uint32      : DD member iSCSI index = 10
++  0814  string      : DD member iSCSI name = "isns.client2"
++--------------
++Object:      index=14 type=<iSCSI Storage Node> state=limbo
++  0020  string      : iSCSI name = "iqn.com.foobar:disk1"
++  0024  uint32      : iSCSI node index = 14
+diff --git a/utils/open-isns/tests/data/test02/15-dd-deregistration b/utils/open-isns/tests/data/test02/15-dd-deregistration
+new file mode 100644
+index 0000000..d9b420f
+--- /dev/null
++++ b/utils/open-isns/tests/data/test02/15-dd-deregistration
+@@ -0,0 +1,76 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test02/server0/database
++Last EID:    1
++Last Index:  15
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client2.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client2"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++  0608v uint32      : Policy allowed node type = Target
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:24:04 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 6
++--------------
++Object:      index=7 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 127.1.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 7
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.1.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
++--------------
++Object:      index=9 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client2.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:24:04 2007
++  0007  uint32      : Entity index = 9
++--------------
++Object:      index=10 type=<iSCSI Storage Node> state=mature parent=9
++  0020  string      : iSCSI name = "isns.client2"
++  0021  uint32      : iSCSI node type = Target
++  0024  uint32      : iSCSI node index = 10
++--------------
++Object:      index=11 type=<Portal> state=mature parent=9
++  0010  ipaddr      : Portal IP address = 127.1.0.2
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 11
++--------------
++Object:      index=12 type=<iSCSI Portal Group> state=mature parent=9
++  0030  string      : Portal group name = "isns.client2"
++  0031  ipaddr      : Portal group address = 127.1.0.2
++  0032  uint32      : Portal group port = 3260/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
++--------------
++Object:      index=14 type=<iSCSI Storage Node> state=limbo
++  0020  string      : iSCSI name = "iqn.com.foobar:disk1"
++  0024  uint32      : iSCSI node index = 14
+diff --git a/utils/open-isns/tests/data/test03/01-enroll b/utils/open-isns/tests/data/test03/01-enroll
+new file mode 100644
+index 0000000..0046b41
+--- /dev/null
++++ b/utils/open-isns/tests/data/test03/01-enroll
+@@ -0,0 +1,18 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test03/server0/database
++Last EID:    1
++Last Index:  4
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test03/02-registration b/utils/open-isns/tests/data/test03/02-registration
+new file mode 100644
+index 0000000..11062a6
+--- /dev/null
++++ b/utils/open-isns/tests/data/test03/02-registration
+@@ -0,0 +1,42 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test03/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:36:35 2007
++  0007  uint32      : Entity index = 4
++--------------
++Object:      index=5 type=<iSCSI Storage Node> state=mature parent=4
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 5
++--------------
++Object:      index=6 type=<Portal> state=mature parent=4
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 6
++--------------
++Object:      index=7 type=<iSCSI Portal Group> state=mature parent=4
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 7
+diff --git a/utils/open-isns/tests/data/test03/03-unregistration b/utils/open-isns/tests/data/test03/03-unregistration
+new file mode 100644
+index 0000000..874235b
+--- /dev/null
++++ b/utils/open-isns/tests/data/test03/03-unregistration
+@@ -0,0 +1,42 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test03/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:25:47 2007
++  0007  uint32      : Entity index = 4
++--------------
++Object:      index=5 type=<iSCSI Storage Node> state=mature parent=4
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 5
++--------------
++Object:      index=6 type=<Portal> state=limbo
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 6
++--------------
++Object:      index=7 type=<iSCSI Portal Group> state=mature parent=4
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 7
+diff --git a/utils/open-isns/tests/data/test03/04-unregistration b/utils/open-isns/tests/data/test03/04-unregistration
+new file mode 100644
+index 0000000..efe63e4
+--- /dev/null
++++ b/utils/open-isns/tests/data/test03/04-unregistration
+@@ -0,0 +1,18 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test03/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test03/99-unregistration b/utils/open-isns/tests/data/test03/99-unregistration
+new file mode 100644
+index 0000000..9ea63b1
+--- /dev/null
++++ b/utils/open-isns/tests/data/test03/99-unregistration
+@@ -0,0 +1,13 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test03/server0/database
++Last EID:    1
++Last Index:  7
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
+diff --git a/utils/open-isns/tests/data/test04/01-enroll b/utils/open-isns/tests/data/test04/01-enroll
+new file mode 100644
+index 0000000..3caf7a2
+--- /dev/null
++++ b/utils/open-isns/tests/data/test04/01-enroll
+@@ -0,0 +1,18 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test04/server0/database
++Last EID:    1
++Last Index:  4
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test04/02-registration b/utils/open-isns/tests/data/test04/02-registration
+new file mode 100644
+index 0000000..0780d23
+--- /dev/null
++++ b/utils/open-isns/tests/data/test04/02-registration
+@@ -0,0 +1,42 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test04/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:38:41 2007
++  0007  uint32      : Entity index = 4
++--------------
++Object:      index=5 type=<iSCSI Storage Node> state=mature parent=4
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 5
++--------------
++Object:      index=6 type=<Portal> state=mature parent=4
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 6
++--------------
++Object:      index=7 type=<iSCSI Portal Group> state=mature parent=4
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 7
+diff --git a/utils/open-isns/tests/data/test04/03-restart b/utils/open-isns/tests/data/test04/03-restart
+new file mode 100644
+index 0000000..0780d23
+--- /dev/null
++++ b/utils/open-isns/tests/data/test04/03-restart
+@@ -0,0 +1,42 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test04/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:38:41 2007
++  0007  uint32      : Entity index = 4
++--------------
++Object:      index=5 type=<iSCSI Storage Node> state=mature parent=4
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 5
++--------------
++Object:      index=6 type=<Portal> state=mature parent=4
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 6
++--------------
++Object:      index=7 type=<iSCSI Portal Group> state=mature parent=4
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 7
+diff --git a/utils/open-isns/tests/data/test04/04-query b/utils/open-isns/tests/data/test04/04-query
+new file mode 100644
+index 0000000..3320a8c
+--- /dev/null
++++ b/utils/open-isns/tests/data/test04/04-query
+@@ -0,0 +1,20 @@
++object[0] = <Network Entity>
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:38:42 2007
++  0007  uint32      : Entity index = 4
++object[1] = <iSCSI Storage Node>
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 5
++object[2] = <Portal>
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 6
++object[3] = <iSCSI Portal Group>
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 7
+diff --git a/utils/open-isns/tests/data/test05/01-enroll b/utils/open-isns/tests/data/test05/01-enroll
+new file mode 100644
+index 0000000..421d125
+--- /dev/null
++++ b/utils/open-isns/tests/data/test05/01-enroll
+@@ -0,0 +1,18 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test05/server0/database
++Last EID:    1
++Last Index:  4
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test05/02-registration b/utils/open-isns/tests/data/test05/02-registration
+new file mode 100644
+index 0000000..8be9f56
+--- /dev/null
++++ b/utils/open-isns/tests/data/test05/02-registration
+@@ -0,0 +1,42 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test05/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 20
++  0004  uint64      : Timestamp = Fri Sep 14 13:40:40 2007
++  0007  uint32      : Entity index = 4
++--------------
++Object:      index=5 type=<iSCSI Storage Node> state=mature parent=4
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 5
++--------------
++Object:      index=6 type=<Portal> state=mature parent=4
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 6
++--------------
++Object:      index=7 type=<iSCSI Portal Group> state=mature parent=4
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 7
+diff --git a/utils/open-isns/tests/data/test05/03-expired b/utils/open-isns/tests/data/test05/03-expired
+new file mode 100644
+index 0000000..1c8b6ea
+--- /dev/null
++++ b/utils/open-isns/tests/data/test05/03-expired
+@@ -0,0 +1,18 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test05/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test06/01-enroll b/utils/open-isns/tests/data/test06/01-enroll
+new file mode 100644
+index 0000000..5ededae
+--- /dev/null
++++ b/utils/open-isns/tests/data/test06/01-enroll
+@@ -0,0 +1,18 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test06/server0/database
++Last EID:    1
++Last Index:  4
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test06/02-registration b/utils/open-isns/tests/data/test06/02-registration
+new file mode 100644
+index 0000000..5b6ab13
+--- /dev/null
++++ b/utils/open-isns/tests/data/test06/02-registration
+@@ -0,0 +1,42 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test06/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:41:51 2007
++  0007  uint32      : Entity index = 4
++--------------
++Object:      index=5 type=<iSCSI Storage Node> state=mature parent=4
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 5
++--------------
++Object:      index=6 type=<Portal> state=mature parent=4
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 6
++--------------
++Object:      index=7 type=<iSCSI Portal Group> state=mature parent=4
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 7
+diff --git a/utils/open-isns/tests/data/test06/03-registration b/utils/open-isns/tests/data/test06/03-registration
+new file mode 100644
+index 0000000..ad555dc
+--- /dev/null
++++ b/utils/open-isns/tests/data/test06/03-registration
+@@ -0,0 +1,42 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test06/server0/database
++Last EID:    1
++Last Index:  12
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=8 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:41:51 2007
++  0007  uint32      : Entity index = 8
++--------------
++Object:      index=9 type=<iSCSI Storage Node> state=mature parent=8
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 9
++--------------
++Object:      index=10 type=<Portal> state=mature parent=8
++  0010  ipaddr      : Portal IP address = 192.168.1.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 10
++--------------
++Object:      index=11 type=<iSCSI Portal Group> state=mature parent=8
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 192.168.1.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 11
+diff --git a/utils/open-isns/tests/data/test06/04-registration b/utils/open-isns/tests/data/test06/04-registration
+new file mode 100644
+index 0000000..477248d
+--- /dev/null
++++ b/utils/open-isns/tests/data/test06/04-registration
+@@ -0,0 +1,42 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test06/server0/database
++Last EID:    1
++Last Index:  16
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=12 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:41:51 2007
++  0007  uint32      : Entity index = 12
++--------------
++Object:      index=13 type=<iSCSI Storage Node> state=mature parent=12
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 13
++--------------
++Object:      index=14 type=<Portal> state=mature parent=12
++  0010  ipaddr      : Portal IP address = 192.168.1.2
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 14
++--------------
++Object:      index=15 type=<iSCSI Portal Group> state=mature parent=12
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 192.168.1.2
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 15
+diff --git a/utils/open-isns/tests/data/test06/05-dd-registration b/utils/open-isns/tests/data/test06/05-dd-registration
+new file mode 100644
+index 0000000..01776db
+--- /dev/null
++++ b/utils/open-isns/tests/data/test06/05-dd-registration
+@@ -0,0 +1,49 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test06/server0/database
++Last EID:    1
++Last Index:  17
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=12 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:41:52 2007
++  0007  uint32      : Entity index = 12
++--------------
++Object:      index=13 type=<iSCSI Storage Node> state=mature parent=12
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 13
++--------------
++Object:      index=14 type=<Portal> state=mature parent=12
++  0010  ipaddr      : Portal IP address = 192.168.1.2
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 14
++--------------
++Object:      index=15 type=<iSCSI Portal Group> state=mature parent=12
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 192.168.1.2
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 15
++--------------
++Object:      index=16 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 13
++  0814  string      : DD member iSCSI name = "isns.client1"
+diff --git a/utils/open-isns/tests/data/test06/06-registration b/utils/open-isns/tests/data/test06/06-registration
+new file mode 100644
+index 0000000..6da36e2
+--- /dev/null
++++ b/utils/open-isns/tests/data/test06/06-registration
+@@ -0,0 +1,49 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test06/server0/database
++Last EID:    1
++Last Index:  20
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=17 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:41:52 2007
++  0007  uint32      : Entity index = 17
++--------------
++Object:      index=13 type=<iSCSI Storage Node> state=mature parent=17
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 13
++--------------
++Object:      index=18 type=<Portal> state=mature parent=17
++  0010  ipaddr      : Portal IP address = 192.168.1.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 18
++--------------
++Object:      index=19 type=<iSCSI Portal Group> state=mature parent=17
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 192.168.1.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 19
++--------------
++Object:      index=16 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 13
++  0814  string      : DD member iSCSI name = "isns.client1"
+diff --git a/utils/open-isns/tests/data/test06/07-dd-registration b/utils/open-isns/tests/data/test06/07-dd-registration
+new file mode 100644
+index 0000000..b3201d2
+--- /dev/null
++++ b/utils/open-isns/tests/data/test06/07-dd-registration
+@@ -0,0 +1,52 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test06/server0/database
++Last EID:    1
++Last Index:  20
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=17 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:41:52 2007
++  0007  uint32      : Entity index = 17
++--------------
++Object:      index=13 type=<iSCSI Storage Node> state=mature parent=17
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 13
++--------------
++Object:      index=18 type=<Portal> state=mature parent=17
++  0010  ipaddr      : Portal IP address = 192.168.1.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 18
++--------------
++Object:      index=19 type=<iSCSI Portal Group> state=mature parent=17
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 192.168.1.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 19
++--------------
++Object:      index=16 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 13
++  0814  string      : DD member iSCSI name = "isns.client1"
++  0816  uint32      : DD member portal index = 18
++  0817  ipaddr      : DD member portal addr = 192.168.1.1
++  0818  uint32      : DD member portal port = 860/tcp
+diff --git a/utils/open-isns/tests/data/test06/08-registration b/utils/open-isns/tests/data/test06/08-registration
+new file mode 100644
+index 0000000..f965777
+--- /dev/null
++++ b/utils/open-isns/tests/data/test06/08-registration
+@@ -0,0 +1,64 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test06/server0/database
++Last EID:    1
++Last Index:  22
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=17 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:41:52 2007
++  0007  uint32      : Entity index = 17
++--------------
++Object:      index=13 type=<iSCSI Storage Node> state=mature parent=17
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 13
++--------------
++Object:      index=18 type=<Portal> state=limbo
++  0010  ipaddr      : Portal IP address = 192.168.1.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 18
++--------------
++Object:      index=19 type=<iSCSI Portal Group> state=mature parent=17
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 192.168.1.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 19
++--------------
++Object:      index=16 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 13
++  0814  string      : DD member iSCSI name = "isns.client1"
++  0816  uint32      : DD member portal index = 18
++  0817  ipaddr      : DD member portal addr = 192.168.1.1
++  0818  uint32      : DD member portal port = 860/tcp
++--------------
++Object:      index=20 type=<Portal> state=mature parent=17
++  0010  ipaddr      : Portal IP address = 192.168.1.2
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 20
++--------------
++Object:      index=21 type=<iSCSI Portal Group> state=mature parent=17
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 192.168.1.2
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 21
+diff --git a/utils/open-isns/tests/data/test06/09-registration b/utils/open-isns/tests/data/test06/09-registration
+new file mode 100644
+index 0000000..1308d3c
+--- /dev/null
++++ b/utils/open-isns/tests/data/test06/09-registration
+@@ -0,0 +1,64 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test06/server0/database
++Last EID:    1
++Last Index:  22
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=17 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:41:52 2007
++  0007  uint32      : Entity index = 17
++--------------
++Object:      index=13 type=<iSCSI Storage Node> state=mature parent=17
++  0020  string      : iSCSI name = "isns.client1"
++  0021  uint32      : iSCSI node type = Initiator
++  0024  uint32      : iSCSI node index = 13
++--------------
++Object:      index=18 type=<Portal> state=mature parent=17
++  0010  ipaddr      : Portal IP address = 192.168.1.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 18
++--------------
++Object:      index=19 type=<iSCSI Portal Group> state=mature parent=17
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 192.168.1.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 19
++--------------
++Object:      index=16 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 13
++  0814  string      : DD member iSCSI name = "isns.client1"
++  0816  uint32      : DD member portal index = 18
++  0817  ipaddr      : DD member portal addr = 192.168.1.1
++  0818  uint32      : DD member portal port = 860/tcp
++--------------
++Object:      index=20 type=<Portal> state=limbo
++  0010  ipaddr      : Portal IP address = 192.168.1.2
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 20
++--------------
++Object:      index=21 type=<iSCSI Portal Group> state=mature parent=17
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 192.168.1.2
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 21
+diff --git a/utils/open-isns/tests/data/test06/10-unregistration b/utils/open-isns/tests/data/test06/10-unregistration
+new file mode 100644
+index 0000000..0c42d1c
+--- /dev/null
++++ b/utils/open-isns/tests/data/test06/10-unregistration
+@@ -0,0 +1,37 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test06/server0/database
++Last EID:    1
++Last Index:  22
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=13 type=<iSCSI Storage Node> state=limbo
++  0020  string      : iSCSI name = "isns.client1"
++  0024  uint32      : iSCSI node index = 13
++--------------
++Object:      index=18 type=<Portal> state=limbo
++  0010  ipaddr      : Portal IP address = 192.168.1.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 18
++--------------
++Object:      index=16 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 13
++  0814  string      : DD member iSCSI name = "isns.client1"
++  0816  uint32      : DD member portal index = 18
++  0817  ipaddr      : DD member portal addr = 192.168.1.1
++  0818  uint32      : DD member portal port = 860/tcp
+diff --git a/utils/open-isns/tests/data/test06/11-registration b/utils/open-isns/tests/data/test06/11-registration
+new file mode 100644
+index 0000000..d8d03f8
+--- /dev/null
++++ b/utils/open-isns/tests/data/test06/11-registration
+@@ -0,0 +1,52 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test06/server0/database
++Last EID:    1
++Last Index:  24
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b7 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=22 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 21 11:23:54 2007
++  0007  uint32      : Entity index = 22
++--------------
++Object:      index=13 type=<iSCSI Storage Node> state=mature parent=22
++  0020  string      : iSCSI name = "isns.client1"
++  0024  uint32      : iSCSI node index = 13
++  0021  uint32      : iSCSI node type = Initiator
++--------------
++Object:      index=18 type=<Portal> state=mature parent=22
++  0010  ipaddr      : Portal IP address = 192.168.1.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 18
++--------------
++Object:      index=23 type=<iSCSI Portal Group> state=mature parent=22
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 192.168.1.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 23
++--------------
++Object:      index=16 type=<Discovery Domain> state=mature
++  0811  uint32      : DD ID = 1
++  0812  string      : DD name = "isns.dd1"
++  081e  uint32      : DD features = <NIL>
++  0813  uint32      : DD member iSCSI index = 13
++  0814  string      : DD member iSCSI name = "isns.client1"
++  0816  uint32      : DD member portal index = 18
++  0817  ipaddr      : DD member portal addr = 192.168.1.1
++  0818  uint32      : DD member portal port = 860/tcp
+diff --git a/utils/open-isns/tests/data/test07/01-enroll b/utils/open-isns/tests/data/test07/01-enroll
+new file mode 100644
+index 0000000..ad2eaf4
+--- /dev/null
++++ b/utils/open-isns/tests/data/test07/01-enroll
+@@ -0,0 +1,19 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test07/server0/database
++Last EID:    1
++Last Index:  4
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++  0004  uint64      : Timestamp = Fri Sep 14 13:42:57 2007
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test07/02-registration b/utils/open-isns/tests/data/test07/02-registration
+new file mode 100644
+index 0000000..da8962a
+--- /dev/null
++++ b/utils/open-isns/tests/data/test07/02-registration
+@@ -0,0 +1,45 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test07/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++  0004  uint64      : Timestamp = Fri Sep 14 13:42:57 2007
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=4 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:42:57 2007
++  0007  uint32      : Entity index = 4
++--------------
++Object:      index=5 type=<iSCSI Storage Node> state=mature parent=4
++  0020  string      : iSCSI name = "isns.client1"
++  0024  uint32      : iSCSI node index = 5
++  0021  uint32      : iSCSI node type = Initiator
++--------------
++Object:      index=6 type=<Portal> state=mature parent=4
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 6
++  0014  uint32      : ESI port = 65535/tcp
++  0013  uint32      : ESI interval = 5
++--------------
++Object:      index=7 type=<iSCSI Portal Group> state=mature parent=4
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 7
+diff --git a/utils/open-isns/tests/data/test07/03-expired b/utils/open-isns/tests/data/test07/03-expired
+new file mode 100644
+index 0000000..edb9ea4
+--- /dev/null
++++ b/utils/open-isns/tests/data/test07/03-expired
+@@ -0,0 +1,19 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test07/server0/database
++Last EID:    1
++Last Index:  8
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++  0004  uint64      : Timestamp = Fri Sep 14 13:42:57 2007
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test07/04-registration b/utils/open-isns/tests/data/test07/04-registration
+new file mode 100644
+index 0000000..de57cbd
+--- /dev/null
++++ b/utils/open-isns/tests/data/test07/04-registration
+@@ -0,0 +1,57 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test07/server0/database
++Last EID:    1
++Last Index:  14
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++  0004  uint64      : Timestamp = Fri Sep 14 13:43:12 2007
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
++--------------
++Object:      index=8 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "client1.isns-test.eu"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 14 13:43:12 2007
++  0007  uint32      : Entity index = 8
++--------------
++Object:      index=9 type=<iSCSI Storage Node> state=mature parent=8
++  0020  string      : iSCSI name = "isns.client1"
++  0024  uint32      : iSCSI node index = 9
++  0021  uint32      : iSCSI node type = Initiator
++--------------
++Object:      index=10 type=<Portal> state=mature parent=8
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 860/tcp
++  0016  uint32      : Portal index = 10
++  0014  uint32      : ESI port = 65535/tcp
++  0013  uint32      : ESI interval = 5
++--------------
++Object:      index=11 type=<Portal> state=mature parent=8
++  0010  ipaddr      : Portal IP address = 127.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 1/tcp
++  0016  uint32      : Portal index = 11
++--------------
++Object:      index=12 type=<iSCSI Portal Group> state=mature parent=8
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 860/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 12
++--------------
++Object:      index=13 type=<iSCSI Portal Group> state=mature parent=8
++  0030  string      : Portal group name = "isns.client1"
++  0031  ipaddr      : Portal group address = 127.0.0.1
++  0032  uint32      : Portal group port = 1/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 13
+diff --git a/utils/open-isns/tests/data/test07/05-expired b/utils/open-isns/tests/data/test07/05-expired
+new file mode 100644
+index 0000000..fd51f78
+--- /dev/null
++++ b/utils/open-isns/tests/data/test07/05-expired
+@@ -0,0 +1,19 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test07/server0/database
++Last EID:    1
++Last Index:  14
++--------------
++Object:      index=1 type=<Network Entity> state=mature PRIVATE
++  0001  string      : Entity identifier = "CONTROL"
++  0007  uint32      : Entity index = 1
++  0004  uint64      : Timestamp = Fri Sep 14 13:43:12 2007
++--------------
++Object:      index=2 type=<Policy> state=mature parent=1 PRIVATE
++  0601v string      : Security Policy Index = "client1.isns-test.eu"
++  0607v string      : Policy allowed node name = "isns.client1"
++  0603v opaque      : DSA security key = <30 82 01 b6 30 82 01 2b 06 07 2a 86 48 ce 38 04 01 30 82 01...
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1 PRIVATE
++  0020  string      : iSCSI name = "isns.control"
++  0021  uint32      : iSCSI node type = <NIL>
++  0024  uint32      : iSCSI node index = 3
+diff --git a/utils/open-isns/tests/data/test08/01-pauw1 b/utils/open-isns/tests/data/test08/01-pauw1
+new file mode 100644
+index 0000000..3de54da
+--- /dev/null
++++ b/utils/open-isns/tests/data/test08/01-pauw1
+@@ -0,0 +1,100 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test08/server0/database
++Last EID:    1
++Last Index:  15
++--------------
++Object:      index=1 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "cyan.pauw.homeunix.net"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Mon Sep 17 15:15:41 2007
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<iSCSI Storage Node> state=mature parent=1
++  0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"
++  0024  uint32      : iSCSI node index = 2
++  0021  uint32      : iSCSI node type = Target
++  0022  string      : iSCSI alias = "Test (10 GB)"
++  002a  string      : iSCSI auth method = "None"
++--------------
++Object:      index=3 type=<iSCSI Storage Node> state=mature parent=1
++  0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1"
++  0024  uint32      : iSCSI node index = 3
++  0021  uint32      : iSCSI node type = Target
++  0022  string      : iSCSI alias = "160 GB disk (ntfs)"
++  002a  string      : iSCSI auth method = "None"
++--------------
++Object:      index=4 type=<iSCSI Storage Node> state=mature parent=1
++  0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2"
++  0024  uint32      : iSCSI node index = 4
++  0021  uint32      : iSCSI node type = Target
++  0022  string      : iSCSI alias = "160 GB disk (ext3)"
++  002a  string      : iSCSI auth method = "CHAP"
++--------------
++Object:      index=5 type=<iSCSI Storage Node> state=mature parent=1
++  0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3"
++  0024  uint32      : iSCSI node index = 5
++  0021  uint32      : iSCSI node type = Target
++  0022  string      : iSCSI alias = "Test (1 GB)"
++  002a  string      : iSCSI auth method = "None"
++--------------
++Object:      index=6 type=<iSCSI Storage Node> state=mature parent=1
++  0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4"
++  0024  uint32      : iSCSI node index = 6
++  0021  uint32      : iSCSI node type = Target
++  0022  string      : iSCSI alias = "Test (40 GB)"
++  002a  string      : iSCSI auth method = "CHAP"
++--------------
++Object:      index=7 type=<iSCSI Storage Node> state=mature parent=1
++  0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5"
++  0024  uint32      : iSCSI node index = 7
++  0021  uint32      : iSCSI node type = Target
++  0022  string      : iSCSI alias = "test"
++  002a  string      : iSCSI auth method = "None"
++--------------
++Object:      index=8 type=<Portal> state=mature parent=1
++  0010  ipaddr      : Portal IP address = 10.0.0.1
++  0011  uint32      : Portal TCP/UDP port = 3260/tcp
++  0016  uint32      : Portal index = 8
++--------------
++Object:      index=9 type=<iSCSI Portal Group> state=mature parent=1
++  0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"
++  0031  ipaddr      : Portal group address = 10.0.0.1
++  0032  uint32      : Portal group port = 3260/tcp
++  0034  uint32      : Portal group index = 9
++  0033  uint32      : Portal group tag = 1
++--------------
++Object:      index=10 type=<iSCSI Portal Group> state=mature parent=1
++  0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1"
++  0031  ipaddr      : Portal group address = 10.0.0.1
++  0032  uint32      : Portal group port = 3260/tcp
++  0034  uint32      : Portal group index = 10
++  0033  uint32      : Portal group tag = 1
++--------------
++Object:      index=11 type=<iSCSI Portal Group> state=mature parent=1
++  0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2"
++  0031  ipaddr      : Portal group address = 10.0.0.1
++  0032  uint32      : Portal group port = 3260/tcp
++  0034  uint32      : Portal group index = 11
++  0033  uint32      : Portal group tag = 1
++--------------
++Object:      index=12 type=<iSCSI Portal Group> state=mature parent=1
++  0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3"
++  0031  ipaddr      : Portal group address = 10.0.0.1
++  0032  uint32      : Portal group port = 3260/tcp
++  0034  uint32      : Portal group index = 12
++  0033  uint32      : Portal group tag = 1
++--------------
++Object:      index=13 type=<iSCSI Portal Group> state=mature parent=1
++  0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4"
++  0031  ipaddr      : Portal group address = 10.0.0.1
++  0032  uint32      : Portal group port = 3260/tcp
++  0034  uint32      : Portal group index = 13
++  0033  uint32      : Portal group tag = 1
++--------------
++Object:      index=14 type=<iSCSI Portal Group> state=mature parent=1
++  0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5"
++  0031  ipaddr      : Portal group address = 10.0.0.1
++  0032  uint32      : Portal group port = 3260/tcp
++  0034  uint32      : Portal group index = 14
++  0033  uint32      : Portal group tag = 1
+diff --git a/utils/open-isns/tests/data/test09/01-pauw2 b/utils/open-isns/tests/data/test09/01-pauw2
+new file mode 100644
+index 0000000..9b0a814
+--- /dev/null
++++ b/utils/open-isns/tests/data/test09/01-pauw2
+@@ -0,0 +1,31 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test09/server0/database
++Last EID:    1
++Last Index:  9
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "blue.pauw.homeunix.net"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Mon Sep 17 15:18:04 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 192.168.1.2
++  0011  uint32      : Portal TCP/UDP port = 33849/tcp
++  0016  uint32      : Portal index = 6
++  0014  uint32      : ESI port = 56288/tcp
++  0013  uint32      : ESI interval = 300
++--------------
++Object:      index=7 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
++  0024  uint32      : iSCSI node index = 7
++  0021  uint32      : iSCSI node type = Initiator
++  0022  string      : iSCSI alias = "blue.pauw.homeunix.net"
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "iqn.2005-03.org.open-iscsi:blue"
++  0031  ipaddr      : Portal group address = 192.168.1.2
++  0032  uint32      : Portal group port = 33849/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
+diff --git a/utils/open-isns/tests/data/test10/01-pauw3 b/utils/open-isns/tests/data/test10/01-pauw3
+new file mode 100644
+index 0000000..b7f3b10
+--- /dev/null
++++ b/utils/open-isns/tests/data/test10/01-pauw3
+@@ -0,0 +1,31 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test10/server0/database
++Last EID:    1
++Last Index:  9
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "blue.pauw.homeunix.net"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Mon Sep 17 16:34:30 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 192.168.1.2
++  0011  uint32      : Portal TCP/UDP port = 33849/tcp
++  0016  uint32      : Portal index = 6
++  0014  uint32      : ESI port = 56288/tcp
++  0013  uint32      : ESI interval = 10
++--------------
++Object:      index=7 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
++  0024  uint32      : iSCSI node index = 7
++  0021  uint32      : iSCSI node type = Initiator
++  0022  string      : iSCSI alias = "blue.pauw.homeunix.net"
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "iqn.2005-03.org.open-iscsi:blue"
++  0031  ipaddr      : Portal group address = 192.168.1.2
++  0032  uint32      : Portal group port = 33849/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
+diff --git a/utils/open-isns/tests/data/test10/02-expired b/utils/open-isns/tests/data/test10/02-expired
+new file mode 100644
+index 0000000..b7f3b10
+--- /dev/null
++++ b/utils/open-isns/tests/data/test10/02-expired
+@@ -0,0 +1,31 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test10/server0/database
++Last EID:    1
++Last Index:  9
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "blue.pauw.homeunix.net"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Mon Sep 17 16:34:30 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 192.168.1.2
++  0011  uint32      : Portal TCP/UDP port = 33849/tcp
++  0016  uint32      : Portal index = 6
++  0014  uint32      : ESI port = 56288/tcp
++  0013  uint32      : ESI interval = 10
++--------------
++Object:      index=7 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
++  0024  uint32      : iSCSI node index = 7
++  0021  uint32      : iSCSI node type = Initiator
++  0022  string      : iSCSI alias = "blue.pauw.homeunix.net"
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "iqn.2005-03.org.open-iscsi:blue"
++  0031  ipaddr      : Portal group address = 192.168.1.2
++  0032  uint32      : Portal group port = 33849/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
+diff --git a/utils/open-isns/tests/data/test10/03-pauw3 b/utils/open-isns/tests/data/test10/03-pauw3
+new file mode 100644
+index 0000000..412c5b5
+--- /dev/null
++++ b/utils/open-isns/tests/data/test10/03-pauw3
+@@ -0,0 +1,31 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test10/server0/database
++Last EID:    1
++Last Index:  9
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "blue.pauw.homeunix.net"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Mon Sep 17 16:34:51 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 192.168.1.2
++  0011  uint32      : Portal TCP/UDP port = 33849/tcp
++  0016  uint32      : Portal index = 6
++  0014  uint32      : ESI port = 56288/tcp
++  0013  uint32      : ESI interval = 10
++--------------
++Object:      index=7 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
++  0024  uint32      : iSCSI node index = 7
++  0021  uint32      : iSCSI node type = Initiator
++  0022  string      : iSCSI alias = "blue.pauw.homeunix.net"
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "iqn.2005-03.org.open-iscsi:blue"
++  0031  ipaddr      : Portal group address = 192.168.1.2
++  0032  uint32      : Portal group port = 33849/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
+diff --git a/utils/open-isns/tests/data/test10/04-expired b/utils/open-isns/tests/data/test10/04-expired
+new file mode 100644
+index 0000000..412c5b5
+--- /dev/null
++++ b/utils/open-isns/tests/data/test10/04-expired
+@@ -0,0 +1,31 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test10/server0/database
++Last EID:    1
++Last Index:  9
++--------------
++Object:      index=5 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "blue.pauw.homeunix.net"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Mon Sep 17 16:34:51 2007
++  0007  uint32      : Entity index = 5
++--------------
++Object:      index=6 type=<Portal> state=mature parent=5
++  0010  ipaddr      : Portal IP address = 192.168.1.2
++  0011  uint32      : Portal TCP/UDP port = 33849/tcp
++  0016  uint32      : Portal index = 6
++  0014  uint32      : ESI port = 56288/tcp
++  0013  uint32      : ESI interval = 10
++--------------
++Object:      index=7 type=<iSCSI Storage Node> state=mature parent=5
++  0020  string      : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
++  0024  uint32      : iSCSI node index = 7
++  0021  uint32      : iSCSI node type = Initiator
++  0022  string      : iSCSI alias = "blue.pauw.homeunix.net"
++--------------
++Object:      index=8 type=<iSCSI Portal Group> state=mature parent=5
++  0030  string      : Portal group name = "iqn.2005-03.org.open-iscsi:blue"
++  0031  ipaddr      : Portal group address = 192.168.1.2
++  0032  uint32      : Portal group port = 33849/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 8
+diff --git a/utils/open-isns/tests/data/test11/01-pauw4 b/utils/open-isns/tests/data/test11/01-pauw4
+new file mode 100644
+index 0000000..50c6b92
+--- /dev/null
++++ b/utils/open-isns/tests/data/test11/01-pauw4
+@@ -0,0 +1,32 @@
++Dumping database contents
++Backend:     /tmp/isns-test/test11/server0/database
++Last EID:    1
++Last Index:  5
++--------------
++Object:      index=1 type=<Network Entity> state=mature
++  0001  string      : Entity identifier = "troopa.nki.nl"
++  0002  uint32      : Entity protocol = iSCSI (2)
++  0006  uint32      : Registration Period = 7200
++  0004  uint64      : Timestamp = Fri Sep 21 09:37:10 2007
++  0007  uint32      : Entity index = 1
++--------------
++Object:      index=2 type=<Portal> state=mature parent=1
++  0010  ipaddr      : Portal IP address = 192.168.1.40
++  0011  uint32      : Portal TCP/UDP port = 3229/tcp
++  0017  uint32      : SCN port = 3230/tcp
++  0014  uint32      : ESI port = 3230/tcp
++  0013  uint32      : ESI interval = 300
++  0016  uint32      : Portal index = 2
++--------------
++Object:      index=3 type=<iSCSI Portal Group> state=mature parent=1
++  0030  string      : Portal group name = "iqn.1991-05.com.microsoft:orange"
++  0031  ipaddr      : Portal group address = 192.168.1.40
++  0032  uint32      : Portal group port = 3229/tcp
++  0033  uint32      : Portal group tag = 1
++  0034  uint32      : Portal group index = 3
++--------------
++Object:      index=4 type=<iSCSI Storage Node> state=mature parent=1
++  0020  string      : iSCSI name = "iqn.1991-05.com.microsoft:orange"
++  0021  uint32      : iSCSI node type = Initiator
++  0022  string      : iSCSI alias = "<MS SW iSCSI Initiator>"
++  0024  uint32      : iSCSI node index = 4
+diff --git a/utils/open-isns/tests/genkey b/utils/open-isns/tests/genkey
+new file mode 100644
+index 0000000..36c5eee
+--- /dev/null
++++ b/utils/open-isns/tests/genkey
+@@ -0,0 +1,175 @@
++#!/bin/bash
++# 
++# This is a very simple script to generate a DSA
++# key pair for authenticated iSNS.
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This script is supposed to be run on the iSNS server.
++# For the first time, run as
++#	isnsgenkey -s 1024
++# This will generate a DSA params file, and a DSA private
++# and public key for the server.
++#
++# For each client, generate a key using
++#	isnsgenkey <clientname>
++# where <clientname> is the fully qualified domain name.
++# This script will convert the FQDN to a valid iSNS
++# source name (isns.com.foobar.host)
++
++myname=`basename $0`
++etcdir=/etc/isns
++keystore=$etcdir/keystore
++dsa_parms=$etcdir/dsa.params
++dsa_bits=1024
++opt_force=0
++opt_server=0
++
++function usage {
++	cat <<-EOF >&2
++	$*
++	Usage:
++	  $myname -s [-f] bits
++	  $myname clientname
++	EOF
++	exit 1
++}
++
++function make_isns_name {
++	OFS="$IFS"
++	IFS=.
++	set -- $*
++
++	__result=$1; shift
++	for part; do
++		__result=$part.$__result
++	done
++	echo "isns.$__result"
++	IFS="$OFS"
++}
++
++set -- `getopt b:fk:s $*`
++while [ $# -gt 0 ]; do
++	opt=$1; shift
++	case $opt in
++	--)	break;;
++	-b)	dsa_bits=$1; shift;;
++	-f)	opt_force=1;;
++	-k)	dsa_priv=$1; shift;;
++	-s)	opt_server=1;;
++	*)	usage "Unknown option $opt";;
++	esac
++done
++
++if [ `id -un` != "root" -a $opt_force -eq 0 ]; then
++	echo "$myname: should be run by super user only" >&2
++	exit 1
++fi
++
++# All newly generated files should have restricted
++# access by default.
++umask 077
++
++tmpdir=`mktemp -d /tmp/isnsgenkey.XXXXXX`
++trap "rm -rf $tmpdir" 0 1 2 15
++
++if [ $opt_server -ne 0 ]; then
++	[ $# -eq 1 ] || usage "Expected DSA key length"
++	dsa_bits=$1
++
++	install -m 755 -d $etcdir
++	if [ -z $dsa_priv ]; then
++		dsa_priv=$etcdir/auth_key
++	fi
++	dsa_pub=$dsa_priv.pub
++	dsa_copy=
++else
++	[ $# -eq 1 ] || usage "Expected client name"
++	client=`make_isns_name $1`
++
++	mkdir -p $tmpdir$etcdir
++	# build_client_conf $client > $tmpdir$etcdir/client.conf
++
++	if [ -z $dsa_priv ]; then
++		dsa_priv=$tmpdir$etcdir/auth_key
++	fi
++	dsa_pub=$dsa_priv.pub
++	dsa_copy=$keystore/$client
++fi
++
++if [ -f $dsa_priv -a $opt_force -eq 0 ]; then
++	cat <<-EOF
++
++	------------------------------------------------------------------
++	| There is already a DSA key installed in $dsa_priv.  In order to
++	| generate a new key, please specify the -f [force] option.
++	------------------------------------------------------------------
++	EOF
++	exit 1
++fi
++
++if [ ! -r $dsa_parms ]; then
++	if [ $opt_server -eq 0 ]; then
++		echo "Please run $myname in server-initialization mode first" >&2
++		exit 1
++	fi
++
++	cat <<-EOF
++
++	------------------------------------------------------------------
++	| I will now try to generate a set of DSA parameters. This can be
++	| a slow process, so please be patient.
++	------------------------------------------------------------------
++	EOF
++
++	mkdir -p `dirname $dsa_parms`
++	openssl dsaparam $dsa_bits -out $dsa_parms ||
++		exit 1
++
++	# DSA parameters are public
++	chmod 644 $dsa_parms
++fi
++
++cat <<EOF
++------------------------------------------------------------------
++| I will now try to generate a DSA private key and store it in
++| $dsa_priv.
++|
++| The key will not be protected by a passphrase.
++------------------------------------------------------------------
++EOF
++openssl gendsa -out $dsa_priv $dsa_parms
++openssl dsa -pubout -in $dsa_priv -out $dsa_pub
++chmod 644 $dsa_pub
++
++cat <<EOF
++------------------------------------------------------------------
++| Testing new DSA key
++------------------------------------------------------------------
++EOF
++if ! openssl dgst -dss1 -sign $dsa_priv -out $tmpdir/test-sig /etc/hosts; then
++	echo "DSA signature failed - aborting!" >&2
++	exit 1
++fi
++if ! openssl dgst -dss1 -verify $dsa_pub -signature $tmpdir/test-sig /etc/hosts; then
++	echo "DSA verification failed - aborting!" >&2
++	exit 1
++fi
++od -tx1 $tmpdir/test-sig
++
++if [ $opt_server -eq 0 ]; then
++	echo "Installing DSA public key as $dsa_copy"
++	install -d -m 755 $keystore
++	install -m 644 $dsa_pub $dsa_copy
++	install -m 644 $etcdir/auth_key.pub $tmpdir$etcdir/server.pub
++
++	tarball=auth-$client.tar.gz
++	tar -C $tmpdir -czf $tarball .$etcdir
++
++	cat <<-EOF
++	------------------------------------------------------------------
++	| Successfully packaged $tarball
++	| Please copy this file to client $client and install
++	------------------------------------------------------------------
++	EOF
++fi
+diff --git a/utils/open-isns/tests/harness.pl b/utils/open-isns/tests/harness.pl
+new file mode 100644
+index 0000000..d7ce025
+--- /dev/null
++++ b/utils/open-isns/tests/harness.pl
+@@ -0,0 +1,929 @@
++#!/usr/bin/perl
++
++use Getopt::Long;
++
++$__isns_verbose = 1;
++$__isns_security = 1;
++
++$__isns_bin = "../";
++$__isns_seq = 0;
++$__isns_test_base = '/tmp/isns-test';
++$__isns_test_dir = '/tmp/isns-test/test';
++$__isns_stage = 1;
++$__isns_test_data = '';
++$__isns_test_dump = '';
++$__isns_passed = 0;
++$__isns_failed = 0;
++$__isns_warned = 0;
++@__isns_servers = ();
++
++%__isns_ignore_tag = (
++	"0004"	=> 1,		# Timestamp
++	"0603v"	=> 1,		# DSA public key
++);
++
++sub isns_fail {
++	
++	print "*** FAILURE ***\n";
++	$__isns_failed++;
++
++	my $line;
++	foreach $line (@_) {
++		print "*** $line ***\n";
++	}
++}
++
++sub isns_pass {
++
++	print "*** SUCCESS ***\n" if ($__isns_verbose > 1);
++	$__isns_passed++;
++}
++
++sub isns_warn {
++
++	printf "*** WARNING: %s ***\n", join(' ', @_);
++	$__isns_warned++;
++}
++
++sub isns_die {
++
++	printf "*** TERMINAL FAILURE: %s ***\n", join(' ', @_);
++	$__isns_failed++;
++
++	&isns_finish;
++	die "Test aborted\n";
++}
++
++sub isns_finish {
++
++	my $pid;
++	foreach $pid (@__isns_servers) {
++		kill 15, $pid or &isns_warn("Cannot kill server process (pid=$pid): $!\n");
++	}
++
++	&isns_report;
++}
++
++sub isns_report {
++
++	print "*** Test $__isns_test_name complete.";
++	print " PASSED: $__isns_passed" if ($__isns_passed);
++	print " FAILED: $__isns_failed" if ($__isns_failed);
++	print " WARNINGS: $__isns_warned" if ($__isns_warned);
++	print " ***\n";
++}
++
++sub isns_info {
++
++	print @_ if ($__isns_verbose > 1);
++}
++
++sub isns_notice {
++
++	print @_ if ($__isns_verbose > 0);
++}
++
++sub isns_stage {
++
++	local($name, @msg) = @_;
++
++	if ($name =~ m/^[0-9]/o) {
++		$__isns_stage_name = $name;
++	} else {
++		$__isns_stage_name = sprintf "%02d-%s",
++			$__isns_stage++, $name;
++	}
++	&isns_notice("*** $__isns_stage_name: ", @msg, " ***\n");
++}
++
++sub build_config {
++
++	local($src_file, $dst_file, *__subst) = @_;
++	my $key;
++	my $okey;
++	my $value;
++	my $sepa;
++	my %subst;
++
++	&isns_info("*** Building $src_file -> $dst_file\n");
++
++	# Translate all keys to lower case.
++	foreach $key (keys(%__subst)) {
++		$value = $__subst{$key};
++		$key =~ tr/A-Z/a-z/;
++		$subst{$key} = $value;
++	}
++#	foreach $key (keys(%subst)) {
++#		printf "  %s -> %s\n", $key, $subst{$key};
++#	}
++
++	open IN, "<$src_file" or die "$src_file: $!\n";
++	open OUT, ">$dst_file" or die "$dst_file: $!\n";
++
++	while (<IN>) {
++		$line = $_;
++		if (m:(\S+)(\s*=\s*)(.*):o) {
++			($okey, $sepa, $value) = ($1, $2, $3);
++
++			$key = $okey;
++			$key =~ tr/A-Z/a-z/;
++
++			if ($subst{$key}) {
++				$line = "$okey$sepa$subst{$key}\n";
++			}
++		}
++
++		# Ignore unconfigured lines.
++		next if ($line =~ m/\@[A-Z_]*\@/o);
++		print OUT $line;
++	}
++	close OUT;
++	close IN;
++}
++
++sub get_config_value {
++	local($cfg_file, $item_name) = @_;
++	my $result;
++	my $name;
++	my $value;
++
++	$item_name =~ tr/A-Z/a-z/;
++
++	open IN, "<$cfg_file" or die "$cfg_file: $!\n";
++	while (<IN>) {
++		chop;
++		($name, $value) = split(/\s+=\s+/, $_);
++
++		$name =~ tr/A-Z/a-z/;
++		if ($name eq $item_name) {
++			$result = $value;
++			last;
++		}
++	}
++	close IN;
++
++	return $result;
++}
++
++sub create_key {
++
++	local($keyfile) = @_;
++
++	if ($__isns_security) {
++		&isns_info("*** Creating key at $keyfile\n");
++		system "./genkey -fsk $keyfile 2048 >${keyfile}.log 2>&1";
++	}
++	return $keyfile;
++}
++
++sub create_server {
++
++	local(*override) = @_;
++	my %local_config;
++	my $my_dir;
++	my $handle;
++	my $config;
++
++	$handle = sprintf "server%d", $__isns_seq++;
++	$my_dir = "$__isns_test_dir/${handle}";
++
++	mkdir $my_dir, 0700 or die "Cannot create $my_dir: $!\n";
++
++	$server_addr = "127.0.0.1:7770" unless ($server_addr);
++
++	$config = "$my_dir/config";
++
++	$local_config{"SourceName"} = "isns.$handle";
++	$local_config{"Database"} = "$my_dir/database";
++	$local_config{"BindAddress"} = "$server_addr";
++	$local_config{"PIDFile"} = "$my_dir/pid";
++	$local_config{"ControlSocket"} = "$my_dir/control";
++	$local_config{"Security"} = $__isns_security;
++	$local_config{"AuthKeyFile"} = &create_key("$my_dir/auth_key");
++
++	foreach $key (keys(%override)) {
++		$local_config{$key} = $override{$key};
++	}
++
++	&build_config('server.conf', $config, \%local_config);
++	return $config;
++}
++
++sub create_client {
++
++	local($server_config, $client_address) = @_;
++	my %local_config;
++	my $server_key;
++	my $control_socket;
++	my $server_addr;
++	my $my_dir;
++	my $handle;
++	my $config;
++
++	$handle = sprintf "client%d", $__isns_seq++;
++	$my_dir = "$__isns_test_dir/${handle}";
++
++	mkdir $my_dir, 0700 or die "Cannot create $my_dir: $!\n";
++
++	$control_socket = &get_config_value($server_config, "ControlSocket");
++	$server_addr = &get_config_value($server_config, "BindAddress");
++	$server_addr = "127.0.0.1" unless ($server_addr);
++
++	$config = "$my_dir/config";
++
++	$local_config{"SourceName"} = "isns.$handle";
++	$local_config{"AuthName"} = "$handle.isns-test.eu";
++	$local_config{"ServerAddress"} = $server_addr;
++	$local_config{"ControlSocket"} = $control_socket;
++	$local_config{"BindAddress"} = $client_address if ($client_address);
++	$local_config{"server_config"} = $server_config;
++	$local_config{"Security"} = $__isns_security;
++	$local_config{"AuthKeyFile"} = &create_key("$my_dir/auth_key");
++	$local_config{"ServerKeyFile"} =
++		&get_config_value($server_config, "AuthKeyFile") . ".pub";
++
++	&build_config('client.conf', $config, \%local_config);
++
++	$__isns_data{$config,"server_config"} = $server_config;
++	$__isns_data{$config} = %local_config;
++	return $config;
++}
++
++sub get_logfile {
++
++	local($config) = @_;
++	my $dir;
++
++	$dir = $config;
++	$dir =~ s|/+[^/]+$||o;
++
++	return "$dir/logfile";
++}
++
++sub run_command {
++
++	local(@cmd) = @_;
++	my $status;
++	my $cmd;
++
++	$cmd = join(' ', @cmd);
++	&isns_info("$cmd\n");
++
++	system "$cmd";
++
++	$status = $?;
++	if ($status) {
++		&isns_warn("Command failed, exit status $status");
++		print "*** Command was: $cmd ***\n";
++		return undef;
++	}
++
++	return 1;
++}
++
++sub isns_start_server {
++
++	local($server_config) = @_;
++	my $logfile;
++	my $pidfile;
++	my $pid;
++
++	die "restart_server: missing server config argument!\n"
++		unless(-f $server_config);
++	$logfile = &get_logfile($server_config);
++	$pidfile = &get_config_value($server_config, "PIDFile");
++
++	&isns_info("*** Starting server (logging to $logfile)\n");
++
++	$pid = fork();
++	if ($pid) {
++		my $retry;
++
++		if ($pidfile) {
++			for ($retry = 0; $retry < 5; $retry++) {
++				last if (-f $pidfile);
++				sleep 1;
++			}
++			$pid = `cat $pidfile` if ($pidfile);
++			chop($pid);
++		}
++		&isns_info("*** Started server (pid=$pid) ***\n");
++		push(@__isns_servers, $pid);
++		return $pid;
++	}
++
++	&isns_info("${__isns_bin}isnsd -c $server_config -f -d all\n");
++	exec "${__isns_bin}isnsd -c $server_config -f -d all >$logfile 2>&1 &"
++		or die "Unable to run isnsd: $!\n";
++}
++
++sub isns_stop_server {
++
++	local($pid) = @_;
++	my @list;
++	my $p;
++
++	kill 15, $pid or &isns_warn("Cannot kill server process (pid=$pid): $!\n");
++	foreach $p (@__isns_servers) {
++		append(@list, $p) unless ($p == $pid);
++	}
++	@__isns_servers = @list;
++}
++
++sub isns_restart_server {
++
++	local($pid, $server_config);
++
++	if ($_[0] =~ m:^\d+$:o) {
++		$pid = shift(@_);
++	} else {
++		if ($#__isns_servers < 0) {
++			&isns_warn("isns_restart_server: no server running\n");
++			return 0;
++		}
++		$pid = $__isns_servers[0];
++	}
++	$server_config = shift(@_);
++
++	&isns_stop_server($pid);
++	return &isns_start_server($server_config);
++}
++
++sub isns_verify_db {
++
++	local($stage, $server_config);
++	my $dump_file;
++	my $data_file;
++
++	if ($_[0] =~ m/^\d/o) {
++		$stage = shift(@_);
++	} else {
++		$stage = $__isns_stage_name;
++	}
++	$server_config = shift(@_);
++
++	die "Test case forgot to call test_prep" unless($__isns_test_data);
++
++	$dump_file = "$__isns_test_dump/$stage";
++	unless (&run_command("${__isns_bin}/isnsd -c $server_config --dump-db > $dump_file")) {
++		&isns_fail;
++		return 0;
++	}
++
++	# See if the reference data file exists. If it
++	# doesn't, this means we're priming the test case.
++	# Just copy the dump file.
++	$data_file = "$__isns_test_data/$stage";
++	unless (-f $data_file) {
++		print "*** Saving database dump for stage $stage ***\n";
++		mkdir $__isns_test_data, 0755;
++		system "cp $dump_file $data_file";
++		return 1;
++	}
++
++	&isns_info("*** Verifying database dump for stage $stage ***\n");
++	if (&verify_dump($stage, $data_file, $dump_file)) {
++		&isns_pass;
++	} else {
++		if ($__isns_verbose > 1) {
++			system("diff -u -ITimestamp -I'DSA security key' $data_file $dump_file");
++		}
++		&isns_fail;
++	}
++
++	return 1;
++}
++
++sub verify_db {
++
++	&isns_verify_db(@_);
++}
++
++sub verify_response {
++
++	local($stage, $client_config) = @_;
++	my $dump_file;
++	my $data_file;
++
++	die "Test case forgot to call test_prep" unless($__isns_test_data);
++
++	$dump_file = &get_logfile($client_config);
++
++	# See if the reference data file exists. If it
++	# doesn't, this means we're priming the test case.
++	# Just copy the dump file.
++	$data_file = "$__isns_test_data/$stage";
++	unless (-f $data_file) {
++		print "*** Saving data for stage $stage ***\n";
++		mkdir $__isns_test_data, 0755;
++		system "cp $dump_file $data_file";
++		return 1;
++	}
++
++	&isns_info("*** Verifying data for stage $stage ***\n");
++	if (&verify_query($stage, $data_file, $dump_file)) {
++		&isns_pass;
++	} else {
++		&isns_fail("Query response returns unexpected data");
++		system "cp $dump_file $__isns_test_dump/$stage";
++		print "*** Saved dump as $__isns_test_dump/$stage\n";
++		print "*** Reference data in $data_file\n";
++		if ($__isns_verbose > 1) {
++			system("diff -u -ITimestamp -I'DSA security key' $data_file $dump_file");
++		}
++	}
++
++	return 1;
++}
++
++sub verify_dump {
++
++	local($stage, $data_file, $dump_file) = @_;
++	my $line;
++	my @dump;
++	my @data;
++	my @obj1;
++	my @obj2;
++
++	@dump = &load_dump($dump_file);
++	@data = &load_dump($data_file);
++
++	&skip_header(\@dump);
++	&skip_header(\@data);
++
++	while (1) {
++		$line++;
++
++		@obj1 = &get_next_object(\@dump);
++		@obj2 = &get_next_object(\@data);
++
++		last unless(@obj1 || @obj2);
++
++		unless (@obj1 && @obj2) {
++			print STDERR "*** $stage: Excess data at end of dump\n";
++			return 0;
++		}
++
++		unless (&compare_objects(\@obj1, \@obj2)) {
++			print STDERR "*** Object mismatch (object $line):\n";
++			print STDERR "Expected:\n  ";
++			print STDERR join("\n  ", @obj2), "\n";
++			print STDERR "Got:\n  ";
++			print STDERR join("\n  ", @obj1), "\n";
++			return 0;
++		}
++	}
++
++	if (@data) {
++		print STDERR "*** $stage: Unexpected end of dump at line $line\n";
++		return 0;
++	}
++
++	return 1;
++}
++
++sub skip_header {
++
++	local(*list) = @_;
++	local($_);
++
++	while ($_ = shift(@list)) {
++		last if (/^-/o);
++	}
++}
++
++sub get_next_object {
++
++	local(*list) = @_;
++	local($_, $header, @result);
++	my @tags;
++
++	while ($_ = shift(@list)) {
++		next if (/^-/o);
++		if (/^\s+([0-9a-fv]+)\s+/o) {
++			next if ($__isns_ignore_tag{$1});
++			push(@tags, $_);
++		} else {
++			if (@result) {
++				unshift(@list, $_);
++				last;
++			}
++			push(@result, $_);
++		}
++		#print "### $_\n";
++	}
++
++	if (@tags) {
++		push(@result, sort(@tags));
++	}
++	return @result;
++}
++
++sub compare_objects {
++
++	local(*a, *b) = @_;
++	local($i);
++
++	return 0 unless ($#a == $#b);
++	for ($i = 0; $i <= $#a; $i++) {
++		return 0 unless ($a[$i] eq $b[$i]);
++	}
++
++	return 1;
++}
++
++
++sub verify_query {
++
++	local($stage, $data_file, $dump_file) = @_;
++	my $line;
++	my @dump;
++	my @data;
++
++	@dump = &load_dump($dump_file);
++	@data = &load_dump($data_file);
++
++	while (@dump) {
++		$line++;
++		unless (@data) {
++			print STDERR "*** $stage: Excess data in dump at line $line\n";
++			return 0;
++		}
++
++		$a = shift(@dump);
++		$b = shift(@data);
++		if ($a =~ /^\S/o) {
++			next if ($a eq $b);
++			print STDERR "*** $stage: Mismatch at line $line ***\n";
++			print STDERR "*** Found:    $a\n";
++			print STDERR "*** Expected: $b\n";
++			return 0;
++		}
++
++		($nix, $a_tag, $a_value) = split(/\s+/, $a, 3);
++		($nix, $b_tag, $b_value) = split(/\s+/, $b, 3);
++		if ($a_tag ne $b_tag) {
++			print STDERR "*** $stage: Tag mismatch at line $line\n";
++			print STDERR "*** Found:    $a\n";
++			print STDERR "*** Expected: $b\n";
++			return 0;
++		}
++
++		next if ($__isns_ignore_tag{$a_tag});
++		if ($a_value ne $b_value) {
++			print STDERR "*** $stage: Value mismatch at line $line (tag $a_tag)\n";
++			print STDERR "*** Found:    $a\n";
++			print STDERR "*** Expected: $b\n";
++			return 0;
++		}
++	}
++
++	if (@data) {
++		print STDERR "*** $stage: Unexpected end of dump at line $line\n";
++		return 0;
++	}
++
++	return 1;
++}
++
++sub load_dump {
++
++	local($filename) = @_;
++	my @result;
++
++	open IN, $filename or die "Unable to open $filename: $!\n";
++	while (<IN>) {
++		chop;
++		push(@result, $_);
++	}
++	close IN;
++	return @result;
++}
++
++
++sub run_client {
++
++	local($config, @args) = @_;
++	my $logfile;
++	my $cmd;
++
++	$logfile = &get_logfile($config);
++
++	$cmd = "${__isns_bin}/isnsadm -c $client_config " . join(' ', @args);
++	if (&run_command("$cmd >$logfile")) {
++		return $logfile;
++	}
++	return undef;
++}
++
++sub __isns_enroll_client {
++
++	local($client_config, @extra_args) = @_;
++	my $source_name;
++	my $auth_name;
++	my $auth_key;
++	my @args;
++
++	$source_name = &get_config_value($client_config, "SourceName");
++	$auth_name = &get_config_value($client_config, "AuthName");
++	$auth_key = &get_config_value($client_config, "AuthKeyFile");
++
++	push(@args, "--local --enroll $auth_name node-name=$source_name");
++	push(@args, " key=${auth_key}.pub") if ($auth_key);
++	push(@args, @extra_args) if (@extra_args);
++
++	&run_client($client_config, @args);
++}
++
++sub isns_enroll_client {
++
++	local($client, @args) = @_;
++	my $server;
++
++	$server = $__isns_data{$client,"server_config"};
++	&isns_stage("enroll", "Enrolling client");
++	&__isns_enroll_client($client, @args);
++	&verify_db($__isns_stage_name, $server);
++}
++
++sub enroll_client {
++
++	print "*** Enrolling client ***\n";
++	&__isns_enroll_client(@_);
++}
++
++sub __isns_register_client {
++
++	local($client_config, @extra_args) = @_;
++	my @args;
++
++	push(@args, "--register");
++	push(@args, @extra_args) if (@extra_args);
++
++	&run_client($client_config, @args);
++}
++
++sub isns_register_client {
++
++	local($client, @args) = @_;
++	my $server;
++
++	$server = $__isns_data{$client,"server_config"};
++	&isns_stage("registration", "Registering client " . join(' ', @args));
++	&__isns_register_client($client, @args);
++	&verify_db($__isns_stage_name, $server);
++}
++
++sub register_client {
++
++	print "*** Registering client ***\n";
++	&__isns_register_client(@_);
++}
++
++sub __isns_query_objects {
++
++	local($client_config, @extra_args) = @_;
++	my @args;
++
++	push(@args, "--query");
++	push(@args, @extra_args) if (@extra_args);
++
++	return &run_client($client_config, @args);
++}
++
++sub isns_query_objects {
++
++	local($client, @args) = @_;
++
++	&isns_stage("query", "Querying " . join(' ', @args));
++	&__isns_query_objects($client, @args);
++	&verify_response($__isns_stage_name, $client);
++}
++
++sub query_objects {
++
++	print "*** Querying objects ***\n";
++	__isns_query_objects(@_);
++}
++
++sub isns_query_eid {
++
++	local($client_config, @extra_args) = @_;
++	my $logfile;
++	my @args;
++	local($eid);
++
++	push(@args, "--query-eid");
++	push(@args, @extra_args) if (@extra_args);
++
++	&isns_info("*** Querying for EID ***\n");
++	$logfile = &run_client($client_config, @args);
++
++	if ($logfile) {
++		$eid = `cat $logfile`;
++		unless ($eid) {
++			&isns_fail("Server reports empty EID");
++		}
++		chop($eid);
++	}
++
++	return $eid;
++}
++
++sub __isns_unregister_client {
++
++	local($client_config, @extra_args) = @_;
++	my @args;
++
++	push(@args, "--deregister");
++	push(@args, @extra_args) if (@extra_args);
++
++	&run_client($client_config, @args);
++}
++
++sub isns_unregister_client {
++
++	my $stage = 0;
++	my $client;
++	my $server;
++	my $eid;
++
++	if ($_[0] =~ m/^\d/o) {
++		&isns_stage(shift(@_), "Unregister client");
++	} else {
++		&isns_stage("unregistration", "Unregister client");
++	}
++
++	$client = shift(@_);
++
++	unless (@_) {
++		$eid = &isns_query_eid($client);
++		push(@_, "eid=$eid");
++	}
++
++	&__isns_unregister_client($client, @_);
++
++	$server = $__isns_data{$client,"server_config"};
++	&verify_db($__isns_stage_name, $server);
++}
++
++sub unregister_client {
++
++	&isns_info("*** Unregistering client ***\n");
++	&__isns_unregister_client(@_);
++}
++
++sub __isns_register_domain {
++
++	local($client_config, @extra_args) = @_;
++	my @args;
++
++	push(@args, "--local --dd-register");
++	push(@args, @extra_args) if (@extra_args);
++
++	&run_client($client_config, @args);
++}
++
++sub isns_register_domain {
++
++	local($client, @args) = @_;
++	my $server;
++
++	&isns_stage("dd-registration", "Registering DD " . join(' ', @args));
++	&__isns_register_domain($client, @args);
++
++	$server = $__isns_data{$client,"server_config"};
++	&isns_verify_db($server);
++}
++
++sub register_domain {
++
++	&isns_info("*** Registering DD ***\n");
++	&__isns_register_domain(@_);
++}
++
++sub __isns_deregister_domain {
++
++	local($client_config, @extra_args) = @_;
++	my @args;
++
++	push(@args, "--local --dd-deregister");
++	push(@args, @extra_args) if (@extra_args);
++
++	&run_client($client_config, @args);
++}
++
++sub isns_deregister_domain {
++
++	local($client, @args) = @_;
++	my $server;
++
++	&isns_stage("dd-deregistration", "Deregistering DD (members)" . join(' ', @args));
++	&__isns_deregister_domain($client, @args);
++
++	$server = $__isns_data{$client,"server_config"};
++	&isns_verify_db($server);
++}
++
++sub isns_external_test {
++
++	local($client, @args) = @_;
++	my $logfile;
++	my $stage;
++	my $cmd;
++
++	$logfile = &get_logfile($client);
++
++	$cmd = shift(@args);
++	$stage = $cmd;
++	$stage =~ s:.*/::o;
++
++	$cmd = "${__isns_bin}/$cmd -c $client " . join(' ', @args);
++
++	&isns_stage($stage, "Running external $cmd " . join(' ', @args));
++	unless (&run_command("$cmd >$logfile")) {
++		return undef;
++	}
++
++	$server = $__isns_data{$client,"server_config"};
++	&isns_verify_db($server);
++}
++
++sub __isns_prep_test {
++
++	local($name, $duration, @ARGV) = @_;
++
++	GetOptions('verbose+' => \$__isns_verbose,
++		   "quiet"    => \$__isns_quiet,
++		   "fast"     => \$__isns_quick,
++		   "insecure" => \$__isns_insecure);
++	$__isns_verbose = 0 if ($__isns_quiet);
++	$__isns_security = 0 if ($__isns_insecure);
++
++	if ($__isns_quick && $duration > 15) {
++		print "*** Skipping $name (duration ~ $duration seconds) ***\n";
++		exit(0);
++	}
++
++	print "*** Starting $name ***\n";
++	printf "*** This test case will take about %u sec ***\n", $duration
++		if ($duration);
++	$__isns_test_name = $name;
++	$__isns_test_dir = "$__isns_test_base/$name";
++	$__isns_test_dump = "$__isns_test_dir/dump";
++	$__isns_test_data = "data/$name";
++
++	# Be careful when removing test dir
++	system "rm -rf $__isns_test_dir" if ($__isns_test_dir =~ m:/tmp/:o);
++
++	mkdir $__isns_test_base, 0700;
++	mkdir $__isns_test_dir, 0700;
++	mkdir $__isns_test_dump, 0700;
++}
++
++sub test_prep {
++
++	local($name, @args) = @_;
++
++	__isns_prep_test($name, 0, @args);
++}
++
++sub isns_prep_slow_test {
++
++	__isns_prep_test(@_);
++}
++
++# Sleep for a few seconds, giving the user some dots to keep
++# him occupied.
++sub isns_idle {
++
++	local($time) = @_;
++
++	if ($__isns_verbose == 0) {
++		sleep $time;
++		return;
++	}
++
++	$| = 1;
++	print "Snooze";
++	while ($time--) {
++		print ".";
++		sleep 1;
++	}
++	print "\n";
++	$| = 0;
++}
++
++sub main {
++
++	my $server_config;
++	my $client_config;
++
++	&test_prep;
++
++	$server_config = &create_server;
++	$client_config = &create_client($server_config);
++}
++
++#&main;
++1;
+diff --git a/utils/open-isns/tests/pauw1.c b/utils/open-isns/tests/pauw1.c
+new file mode 100644
+index 0000000..c3e66f7
+--- /dev/null
++++ b/utils/open-isns/tests/pauw1.c
+@@ -0,0 +1,179 @@
++/*
++ * Test case, captured from a Wasabi Storage Builder
++ * registering itself.
++ */
++#include <getopt.h>
++#include <isns.h>
++#include <paths.h>
++#include <util.h>
++#include <message.h>
++
++int
++main(int argc, char **argv)
++{
++	const char	*opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG;
++	isns_client_t	*clnt;
++	isns_attr_list_t *attrs;
++	isns_simple_t	*reg;
++	isns_portal_info_t portal_info;
++	uint32_t	status;
++	int		c;
++
++	while ((c = getopt(argc, argv, "c:d:")) != -1) {
++		switch (c) {
++		case 'c':
++			opt_configfile = optarg;
++			break;
++
++		case 'd':
++			isns_enable_debugging(optarg);
++			break;
++
++		default:
++			isns_fatal("Unknown option\n");
++		}
++	}
++
++	isns_read_config(opt_configfile);
++	isns_assign_string(&isns_config.ic_source_name,
++			"iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0");
++
++	clnt = isns_create_default_client(NULL);
++
++	reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER,
++			clnt->ic_source, NULL);
++
++	attrs = &reg->is_operating_attrs;
++
++#define ADD(type, tag, value) \
++	isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value)
++#define STR(tag, value)		ADD(string, tag, value)
++#define U32(tag, value)		ADD(uint32, tag, value)
++#define NIL(tag)		isns_attr_list_append_nil(attrs, ISNS_TAG_##tag)
++#define TARGET(name, alias, auth) \
++	STR(ISCSI_NAME,		name); \
++	U32(ISCSI_NODE_TYPE,	ISNS_ISCSI_TARGET_MASK); \
++	STR(ISCSI_ALIAS,	alias); \
++	STR(ISCSI_AUTHMETHOD,	auth)
++
++	STR(ENTITY_IDENTIFIER,		"cyan.pauw.homeunix.net");
++	U32(ENTITY_PROTOCOL,		2);
++	U32(REGISTRATION_PERIOD,	31536000);
++
++	TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0",
++	       "Test (10 GB)",
++	       "None");
++	TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1",
++	       "160 GB disk (ntfs)",
++	       "None");
++	TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2",
++	       "160 GB disk (ext3)",
++	       "CHAP");
++	TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3",
++	       "Test (1 GB)",
++	       "None");
++	TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4",
++	       "Test (40 GB)",
++	       "CHAP");
++	TARGET("iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5",
++	       "test",
++	       "None");
++
++	isns_portal_parse(&portal_info, "10.0.0.1:3260/tcp", NULL);
++	isns_portal_to_attr_list(&portal_info,
++			ISNS_TAG_PORTAL_IP_ADDRESS,
++			ISNS_TAG_PORTAL_TCP_UDP_PORT,
++			attrs);
++
++	/* Mumbo jumbo encoding of portal groups */
++	U32(PG_TAG,		1);
++	STR(PG_ISCSI_NAME,	"iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0");
++	STR(PG_ISCSI_NAME,	"iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1");
++	STR(PG_ISCSI_NAME,	"iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2");
++	STR(PG_ISCSI_NAME,	"iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3");
++	STR(PG_ISCSI_NAME,	"iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4");
++	STR(PG_ISCSI_NAME,	"iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5");
++
++	/* Strictly speaking, a PGT not followed by any data is invalid.
++	 *
++	 * 5.6.5.1.
++	 * When a Portal is registered, the Portal attributes MAY
++	 * immediately be followed by a PGT attribute.	The PGT attribute
++	 * SHALL be followed by the set of PG iSCSI Names representing
++	 * nodes that will be associated to the Portal using the indicated
++	 * PGT value.
++	 */
++	NIL(PG_TAG);
++
++	isns_simple_print(reg, isns_print_stdout);
++
++	status = isns_client_call(clnt, &reg);
++
++	if (status != ISNS_SUCCESS)
++		isns_fatal("Unable to register object: %s\n",
++				isns_strerror(status));
++
++	printf("Successfully registered object(s)\n");
++	isns_simple_print(reg, isns_print_stdout);
++
++	return 0;
++}
++
++/*
++   Creating file DB backend (/var/lib/isns)
++   DB: loading all objects from /var/lib/isns
++   Next ESI message in 3600 seconds
++   Incoming PDU xid=0001 seq=0 len=1208 func=DevAttrReg client first last
++   Next message xid=0001
++   Received message
++   ---DevAttrReg---
++   Source:
++     0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"
++   Message attributes: <empty list>
++   Operating attributes:
++     0001  string      : Entity identifier = "cyan.pauw.homeunix.net"
++     0002  uint32      : Entity protocol = iSCSI (2)
++     0006  uint32      : Registration Period = 31536000
++     0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"
++     0021  uint32      : iSCSI node type = Target
++     0022  string      : iSCSI alias = "Test (10 GB)"
++     002a  string      : iSCSI auth method = "None"
++     0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1"
++     0021  uint32      : iSCSI node type = Target
++     0022  string      : iSCSI alias = "160 GB disk (ntfs)"
++     002a  string      : iSCSI auth method = "None"
++     0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2"
++     0021  uint32      : iSCSI node type = Target
++     0022  string      : iSCSI alias = "160 GB disk (ext3)"
++     002a  string      : iSCSI auth method = "CHAP"
++     0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3"
++     0021  uint32      : iSCSI node type = Target
++     0022  string      : iSCSI alias = "Test (1 GB)"
++     002a  string      : iSCSI auth method = "None"
++     0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4"
++     0021  uint32      : iSCSI node type = Target
++     0022  string      : iSCSI alias = "Test (40 GB)"
++     002a  string      : iSCSI auth method = "CHAP"
++     0020  string      : iSCSI name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5"
++     0021  uint32      : iSCSI node type = Target
++     0022  string      : iSCSI alias = "test"
++     002a  string      : iSCSI auth method = "None"
++     0010  ipaddr      : Portal IP address = 10.0.0.1
++     0011  uint32      : Portal TCP/UDP port = 3260/tcp
++     0033  uint32      : Portal group tag = 1
++     0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0"
++     0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-1"
++     0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-2"
++     0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-3"
++     0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-4"
++     0030  string      : Portal group name = "iqn.2000-05.com.wasabisystems.storagebuilder:cyan-5"
++     0033  nil         : Portal group tag = <empty>
++   :: policy insecure function DevAttrReg (0001) permitted
++   :: policy insecure source
++iqn.2000-05.com.wasabisystems.storagebuilder:cyan-0 permitted
++   :: policy insecure operation DevAttrReg on Network Entity object
++permitted
++   DB: Storing object 00000001 -> /var/lib/isns/00000001
++   DB: added object 1 (Network Entity) state 1
++Segmentation fault
++ */
+diff --git a/utils/open-isns/tests/pauw2.c b/utils/open-isns/tests/pauw2.c
+new file mode 100644
+index 0000000..29084b3
+--- /dev/null
++++ b/utils/open-isns/tests/pauw2.c
+@@ -0,0 +1,212 @@
++/*
++ * Test case, captured from iscsi-target
++ * registering itself.
++ */
++#include <getopt.h>
++#include <isns.h>
++#include <paths.h>
++#include <util.h>
++#include <message.h>
++
++#define ADD(type, tag, value) \
++	isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value)
++#define STR(tag, value)		ADD(string, tag, value)
++#define U32(tag, value)		ADD(uint32, tag, value)
++#define NIL(tag)		isns_attr_list_append_nil(attrs, ISNS_TAG_##tag)
++#define TARGET(name, alias, auth) \
++	STR(ISCSI_NAME,		name); \
++	U32(ISCSI_NODE_TYPE,	ISNS_ISCSI_TARGET_MASK); \
++	STR(ISCSI_ALIAS,	alias); \
++	STR(ISCSI_AUTHMETHOD,	auth)
++
++int
++main(int argc, char **argv)
++{
++	const char	*opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG;
++	isns_client_t	*clnt;
++	isns_attr_list_t *attrs;
++	isns_simple_t	*reg;
++	isns_portal_info_t portal_info;
++	uint32_t	status;
++	int		c;
++
++	while ((c = getopt(argc, argv, "c:d:")) != -1) {
++		switch (c) {
++		case 'c':
++			opt_configfile = optarg;
++			break;
++
++		case 'd':
++			isns_enable_debugging(optarg);
++			break;
++
++		default:
++			isns_fatal("Unknown option\n");
++		}
++	}
++
++	isns_read_config(opt_configfile);
++
++	/*
++	    ---DevAttrReg[REPLACE]---
++	    Source:
++	      0020  string      : iSCSI name = "iqn.2007-03.com.example:stgt.disk"
++	    Message attributes:
++	      0001  string      : Entity identifier = "blue.pauw.homeunix.net"
++	    Operating attributes:
++	      0001  string      : Entity identifier = "blue.pauw.homeunix.net"
++	      0002  uint32      : Entity protocol = iSCSI (2)
++	      0010  ipaddr      : Portal IP address = 192.168.1.2
++	      0011  uint32      : Portal TCP/UDP port = 3260/tcp
++	      0017  uint32      : SCN port = 42138/tcp
++	      0020  string      : iSCSI name = "iqn.2007-03.com.example:stgt.disk"
++	      0021  uint32      : iSCSI node type = Target
++	 */
++	isns_assign_string(&isns_config.ic_source_name,
++			"iqn.2007-03.com.example:stgt.disk");
++
++	clnt = isns_create_default_client(NULL);
++	reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER,
++			clnt->ic_source, NULL);
++	reg->is_replace = 1;
++
++	/* Message attributes */
++	attrs = &reg->is_message_attrs;
++	STR(ENTITY_IDENTIFIER,	"blue.pauw.homeunix.net");
++
++	/* Operating attributes */
++	attrs = &reg->is_operating_attrs;
++
++	STR(ENTITY_IDENTIFIER,	"blue.pauw.homeunix.net");
++	U32(ENTITY_PROTOCOL,	2);
++
++	isns_portal_parse(&portal_info, "192.168.1.2:3260/tcp", NULL);
++	isns_portal_to_attr_list(&portal_info,
++			ISNS_TAG_PORTAL_IP_ADDRESS,
++			ISNS_TAG_PORTAL_TCP_UDP_PORT,
++			attrs);
++
++	U32(SCN_PORT,		42138);
++	STR(ISCSI_NAME,		"iqn.2007-03.com.example:stgt.disk");
++	U32(ISCSI_NODE_TYPE,	ISNS_ISCSI_TARGET_MASK);
++	isns_simple_print(reg, isns_print_stdout);
++
++	status = isns_client_call(clnt, &reg);
++
++	if (status != ISNS_SUCCESS)
++		isns_fatal("Unable to register object: %s\n",
++				isns_strerror(status));
++
++	printf("Successfully registered object #1\n");
++	// isns_simple_print(reg, isns_print_stdout);
++	isns_simple_free(reg);
++	isns_client_destroy(clnt);
++
++	/*
++	    ---DevAttrReg[REPLACE]---
++	    Source:
++	      0020  string      : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
++	    Message attributes:
++	      0001  string      : Entity identifier = "blue.pauw.homeunix.net"
++	    Operating attributes:
++	      0001  string      : Entity identifier = "blue.pauw.homeunix.net"
++	      0002  uint32      : Entity protocol = iSCSI (2)
++	      0010  ipaddr      : Portal IP address = 192.168.1.2
++	      0011  uint32      : Portal TCP/UDP port = 33849/tcp
++	      0014  uint32      : ESI port = 56288/tcp
++	      0020  string      : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
++	      0021  uint32      : iSCSI node type = Initiator
++	      0022  string      : iSCSI alias = "blue.pauw.homeunix.net"
++
++	      [...]
++    	      response status 0x0003 (Invalid registration)
++
++	   This would fail because we got confused about EID in
++	   the replace case.
++	 */
++	isns_assign_string(&isns_config.ic_source_name,
++			"iqn.2005-03.org.open-iscsi:blue");
++
++	clnt = isns_create_default_client(NULL);
++	reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER,
++			clnt->ic_source, NULL);
++	reg->is_replace = 1;
++
++	/* Message attributes */
++	attrs = &reg->is_message_attrs;
++	STR(ENTITY_IDENTIFIER,	"blue.pauw.homeunix.net");
++
++	/* Operating attributes */
++	attrs = &reg->is_operating_attrs;
++
++	STR(ENTITY_IDENTIFIER,	"blue.pauw.homeunix.net");
++	U32(ENTITY_PROTOCOL,	2);
++
++	isns_portal_parse(&portal_info, "192.168.1.2:33849/tcp", NULL);
++	isns_portal_to_attr_list(&portal_info,
++			ISNS_TAG_PORTAL_IP_ADDRESS,
++			ISNS_TAG_PORTAL_TCP_UDP_PORT,
++			attrs);
++
++	U32(ESI_PORT,		56288);
++	STR(ISCSI_NAME,		"iqn.2005-03.org.open-iscsi:blue");
++	U32(ISCSI_NODE_TYPE,	ISNS_ISCSI_INITIATOR_MASK);
++	STR(ISCSI_ALIAS,	"blue.pauw.homeunix.net");
++	isns_simple_print(reg, isns_print_stdout);
++
++	status = isns_client_call(clnt, &reg);
++
++	if (status != ISNS_SUCCESS)
++		isns_fatal("Unable to register object: %s\n",
++				isns_strerror(status));
++
++	printf("Successfully registered object #2\n");
++	// isns_simple_print(reg, isns_print_stdout);
++	isns_simple_free(reg);
++	isns_client_destroy(clnt);
++
++	return 0;
++}
++
++/*
++    Creating file DB backend (/var/lib/isns)
++    DB: loading all objects from /var/lib/isns
++    Next ESI message in 3600 seconds
++    Incoming PDU xid=0001 seq=0 len=232 func=DevAttrReg client first last
++    Next message xid=0001
++    Received message
++
++    :: policy insecure function DevAttrReg (0001) permitted
++    :: policy insecure source iqn.2005-03.org.open-iscsi:blue permitted
++    :: policy insecure operation DevAttrReg on object 00000001 (Network 
++Entity) permitted
++    Replacing Network Entity (id 1)
++    DB: removed object 2 (Portal)
++    DB: removed object 4 (iSCSI Portal Group)
++    DB: removed object 3 (iSCSI Storage Node)
++    DB: removed object 1 (Network Entity)
++    DB: destroying object 2 (Portal)
++    DB: Purging object 2 (/var/lib/isns/00000002)
++    DB: destroying object 1 (Network Entity)
++    DB: Purging object 1 (/var/lib/isns/00000001)
++    DB: destroying object 3 (iSCSI Storage Node)
++    DB: Purging object 3 (/var/lib/isns/00000003)
++    DB: destroying object 4 (iSCSI Portal Group)
++    DB: Purging object 4 (/var/lib/isns/00000004)
++    :: policy insecure entity ID blue.pauw.homeunix.net permitted
++    :: policy insecure operation DevAttrReg on Network Entity object 
++permitted
++    DB: Storing object 5 -> /var/lib/isns/00000005
++    DB: added object 5 (Network Entity) state 1
++    DB: Storing object 5 -> /var/lib/isns/00000005
++    isns_esi_callback(0x9dee788, 0x10)
++    Deleting SCN registration for iqn.2007-03.com.example:stgt.disk
++    isns_esi_callback(0x9deeae0, 0x10)
++    isns_esi_callback(0x9deea30, 0x10)
++    isns_esi_callback(0x9deec80, 0x10)
++    SCN multicast <iSCSI Storage Node 3, removed>
++    isns_scn_callback(0x9deec80, 0x10)
++    isns_esi_callback(0x9def4b0, 0xc)
++    Enable ESI monitoring for entity 5
++
++ */
+diff --git a/utils/open-isns/tests/pauw3.c b/utils/open-isns/tests/pauw3.c
+new file mode 100644
+index 0000000..3be0baa
+--- /dev/null
++++ b/utils/open-isns/tests/pauw3.c
+@@ -0,0 +1,139 @@
++/*
++ * This tests another problem reported by Albert, where a
++ * re-registration shortly before ESI expiry would fail
++ * to resurrect the registration properly.
++ *
++ * Usage:
++ *  pauw3 [options] timeout
++ *
++ * Where timeout is the delay until we try to re-register
++ */
++
++#include <getopt.h>
++#include <unistd.h>
++
++#include <isns.h>
++#include <paths.h>
++#include <util.h>
++#include <message.h>
++
++#define ADD(type, tag, value) \
++	isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value)
++#define STR(tag, value)		ADD(string, tag, value)
++#define U32(tag, value)		ADD(uint32, tag, value)
++#define NIL(tag)		isns_attr_list_append_nil(attrs, ISNS_TAG_##tag)
++#define TARGET(name, alias, auth) \
++	STR(ISCSI_NAME,		name); \
++	U32(ISCSI_NODE_TYPE,	ISNS_ISCSI_TARGET_MASK); \
++	STR(ISCSI_ALIAS,	alias); \
++	STR(ISCSI_AUTHMETHOD,	auth)
++
++int
++main(int argc, char **argv)
++{
++	const char	*opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG;
++	isns_client_t	*clnt;
++	isns_attr_list_t *attrs;
++	isns_simple_t	*reg;
++	isns_portal_info_t portal_info;
++	uint32_t	status;
++	int		opt_replace = 1;
++	int		c, n, timeout;
++
++	while ((c = getopt(argc, argv, "c:d:n")) != -1) {
++		switch (c) {
++		case 'c':
++			opt_configfile = optarg;
++			break;
++
++		case 'd':
++			isns_enable_debugging(optarg);
++			break;
++
++		case 'n':
++			opt_replace = 0;
++			break;
++
++		default:
++			isns_fatal("Unknown option\n");
++		}
++	}
++
++	if (optind != argc - 1)
++		isns_fatal("Need timeout argument\n");
++	timeout = parse_timeout(argv[optind]);
++
++	isns_read_config(opt_configfile);
++
++	/*
++	    ---DevAttrReg[REPLACE]---
++	    Source:
++	      0020  string      : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
++	    Message attributes:
++	      0001  string      : Entity identifier = "blue.pauw.homeunix.net"
++	    Operating attributes:
++	      0001  string      : Entity identifier = "blue.pauw.homeunix.net"
++	      0002  uint32      : Entity protocol = iSCSI (2)
++	      0010  ipaddr      : Portal IP address = 192.168.1.2
++	      0011  uint32      : Portal TCP/UDP port = 33849/tcp
++	      0014  uint32      : ESI port = 56288/tcp
++	      0020  string      : iSCSI name = "iqn.2005-03.org.open-iscsi:blue"
++	      0021  uint32      : iSCSI node type = Initiator
++	      0022  string      : iSCSI alias = "blue.pauw.homeunix.net"
++
++	      [...]
++    	      response status 0x0003 (Invalid registration)
++
++	   This would fail because we got confused about EID in
++	   the replace case.
++	 */
++	isns_assign_string(&isns_config.ic_source_name,
++			"iqn.2005-03.org.open-iscsi:blue");
++
++	for (n = 0; n < 2; ++n) {
++		clnt = isns_create_default_client(NULL);
++		reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER,
++				clnt->ic_source, NULL);
++		reg->is_replace = opt_replace;
++
++		/* Message attributes */
++		attrs = &reg->is_message_attrs;
++		STR(ENTITY_IDENTIFIER,	"blue.pauw.homeunix.net");
++
++		/* Operating attributes */
++		attrs = &reg->is_operating_attrs;
++
++		STR(ENTITY_IDENTIFIER,	"blue.pauw.homeunix.net");
++		U32(ENTITY_PROTOCOL,	2);
++
++		isns_portal_parse(&portal_info, "192.168.1.2:33849/tcp", NULL);
++		isns_portal_to_attr_list(&portal_info,
++				ISNS_TAG_PORTAL_IP_ADDRESS,
++				ISNS_TAG_PORTAL_TCP_UDP_PORT,
++				attrs);
++
++		U32(ESI_PORT,		56288);
++		STR(ISCSI_NAME,		"iqn.2005-03.org.open-iscsi:blue");
++		U32(ISCSI_NODE_TYPE,	ISNS_ISCSI_INITIATOR_MASK);
++		STR(ISCSI_ALIAS,	"blue.pauw.homeunix.net");
++		isns_simple_print(reg, isns_print_stdout);
++
++		status = isns_client_call(clnt, &reg);
++
++		if (status != ISNS_SUCCESS)
++			isns_fatal("Unable to register object: %s\n",
++					isns_strerror(status));
++
++		printf("Successfully registered object\n");
++		// isns_simple_print(reg, isns_print_stdout);
++		isns_simple_free(reg);
++		isns_client_destroy(clnt);
++
++		if (n == 0) {
++			printf("Sleeping for %d seconds\n", timeout);
++			sleep(timeout);
++		}
++	}
++
++	return 0;
++}
+diff --git a/utils/open-isns/tests/pauw4.c b/utils/open-isns/tests/pauw4.c
+new file mode 100644
+index 0000000..9510ddd
+--- /dev/null
++++ b/utils/open-isns/tests/pauw4.c
+@@ -0,0 +1,137 @@
++/*
++ * Test MS initiator registration.
++ * The oddity about this is that the PG object precedes the
++ * initiator object in the message.
++ */
++
++#include <getopt.h>
++#include <unistd.h>
++
++#include <isns.h>
++#include <paths.h>
++#include <util.h>
++#include <message.h>
++
++#define ADD(type, tag, value) \
++	isns_attr_list_append_##type(attrs, ISNS_TAG_##tag, value)
++#define STR(tag, value)		ADD(string, tag, value)
++#define U32(tag, value)		ADD(uint32, tag, value)
++#define NIL(tag)		isns_attr_list_append_nil(attrs, ISNS_TAG_##tag)
++#define TARGET(name, alias, auth) \
++	STR(ISCSI_NAME,		name); \
++	U32(ISCSI_NODE_TYPE,	ISNS_ISCSI_TARGET_MASK); \
++	STR(ISCSI_ALIAS,	alias); \
++	STR(ISCSI_AUTHMETHOD,	auth)
++
++int
++main(int argc, char **argv)
++{
++	const char	*opt_configfile = ISNS_DEFAULT_ISNSADM_CONFIG;
++	isns_client_t	*clnt;
++	isns_attr_list_t *attrs;
++	isns_simple_t	*reg;
++	isns_portal_info_t portal_info;
++	uint32_t	status;
++	int		opt_replace = 1;
++	int		c;
++
++	while ((c = getopt(argc, argv, "c:d:n")) != -1) {
++		switch (c) {
++		case 'c':
++			opt_configfile = optarg;
++			break;
++
++		case 'd':
++			isns_enable_debugging(optarg);
++			break;
++
++		case 'n':
++			opt_replace = 0;
++			break;
++
++		default:
++			isns_fatal("Unknown option\n");
++		}
++	}
++
++	isns_read_config(opt_configfile);
++
++	isns_assign_string(&isns_config.ic_source_name,
++			"iqn.1991-05.com.microsoft:orange");
++
++	clnt = isns_create_default_client(NULL);
++
++	reg = isns_simple_create(ISNS_SCN_DEREGISTER, clnt->ic_source, NULL);
++
++	/* Message attributes */
++	attrs = &reg->is_message_attrs;
++	STR(ISCSI_NAME,		"iqn.1991-05.com.microsoft:orange");
++
++	status = isns_client_call(clnt, &reg);
++	if (status != ISNS_SUCCESS)
++		isns_error("SCNDereg failed: %s\n", isns_strerror(status));
++	isns_simple_free(reg);
++
++
++	reg = isns_simple_create(ISNS_DEVICE_DEREGISTER, clnt->ic_source, NULL);
++
++	attrs = &reg->is_operating_attrs;
++	STR(ENTITY_IDENTIFIER,	"troopa.nki.nl");
++	U32(ENTITY_PROTOCOL,	2);
++
++	isns_portal_parse(&portal_info, "192.168.1.40:3229/tcp", NULL);
++	isns_portal_to_attr_list(&portal_info,
++			ISNS_TAG_PORTAL_IP_ADDRESS,
++			ISNS_TAG_PORTAL_TCP_UDP_PORT,
++			attrs);
++
++	STR(ISCSI_NAME,		"iqn.1991-05.com.microsoft:orange");
++
++	status = isns_client_call(clnt, &reg);
++	if (status != ISNS_SUCCESS)
++		isns_fatal("DevDereg failed: %s\n", isns_strerror(status));
++	isns_simple_free(reg);
++
++	reg = isns_simple_create(ISNS_DEVICE_ATTRIBUTE_REGISTER, clnt->ic_source, NULL);
++	reg->is_replace = opt_replace;
++
++	attrs = &reg->is_operating_attrs;
++	STR(ENTITY_IDENTIFIER,	"troopa.nki.nl");
++	U32(ENTITY_PROTOCOL,	2);
++
++	isns_portal_parse(&portal_info, "192.168.1.40:3229/tcp", NULL);
++	isns_portal_to_attr_list(&portal_info,
++			ISNS_TAG_PORTAL_IP_ADDRESS,
++			ISNS_TAG_PORTAL_TCP_UDP_PORT,
++			attrs);
++
++	U32(SCN_PORT,		3230);
++	U32(ESI_PORT,		3230);
++
++	U32(PG_TAG,		1);
++	STR(PG_ISCSI_NAME,	"iqn.1991-05.com.microsoft:orange");
++
++	STR(ISCSI_NAME,		"iqn.1991-05.com.microsoft:orange");
++	U32(ISCSI_NODE_TYPE,	ISNS_ISCSI_INITIATOR_MASK);
++	STR(ISCSI_ALIAS,	"<MS SW iSCSI Initiator>");
++
++	status = isns_client_call(clnt, &reg);
++	if (status != ISNS_SUCCESS)
++		isns_fatal("DevAttrReg failed: %s\n", isns_strerror(status));
++	isns_simple_free(reg);
++
++	reg = isns_simple_create(ISNS_DEVICE_GET_NEXT, clnt->ic_source, NULL);
++	attrs = &reg->is_message_attrs;
++	NIL(ISCSI_NAME);
++
++	attrs = &reg->is_operating_attrs;
++	U32(ISCSI_NODE_TYPE,	ISNS_ISCSI_TARGET_MASK);
++	NIL(ISCSI_NODE_TYPE);
++
++	status = isns_client_call(clnt, &reg);
++	if (status != ISNS_SUCCESS)
++		isns_fatal("DevGetNext failed: %s\n", isns_strerror(status));
++	isns_simple_free(reg);
++
++	return 0;
++}
+diff --git a/utils/open-isns/tests/server.conf b/utils/open-isns/tests/server.conf
+new file mode 100644
+index 0000000..fc0bb5a
+--- /dev/null
++++ b/utils/open-isns/tests/server.conf
+@@ -0,0 +1,11 @@
++BindAddress		= @SERVER_ADDRESS@
++SourceName		= @SOURCE_NAME@
++Database		= @DB_PATH@
++RegistrationPeriod	= 2h
++ESIMinInterval		= 1m
++ESIMinInterval		= 5m
++Security		= @NOT_SET@
++AuthKeyFile		= @AUTH_KEY@
++ClientKeyStore		= DB:
++PIDFile			= @MYDIR@/pid
++ControlSocket		= @MYDIR@/control
+diff --git a/utils/open-isns/tests/test01.pl b/utils/open-isns/tests/test01.pl
+new file mode 100644
+index 0000000..258acff
+--- /dev/null
++++ b/utils/open-isns/tests/test01.pl
+@@ -0,0 +1,30 @@
++#!/usr/bin/perl
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This test case validates registration and simple query of
++# single client.
++
++push(@INC, ".");
++require "harness.pl";
++
++&test_prep("test01", @ARGV);
++
++$server = &create_server;
++$client = &create_client($server);
++
++&isns_start_server($server);
++
++# 1: Enroll the test client
++&isns_enroll_client($client);
++
++# 2: Register an initiator with default portal
++&isns_register_client($client, "initiator portal");
++
++# 3: Run a simple query
++&isns_query_objects($client, "eid");
++
++# 99: Unregister client
++&isns_unregister_client("99-unregistration", $client);
++
++&isns_finish;
+diff --git a/utils/open-isns/tests/test02.pl b/utils/open-isns/tests/test02.pl
+new file mode 100644
+index 0000000..208bed5
+--- /dev/null
++++ b/utils/open-isns/tests/test02.pl
+@@ -0,0 +1,58 @@
++#!/usr/bin/perl
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This test case validates registration and simple query of
++# two clients, and simple DD functionality.
++
++push(@INC, ".");
++require "harness.pl";
++
++&test_prep("test02", @ARGV);
++
++$server = &create_server;
++$client1 = &create_client($server, "127.1.0.1");
++$client2 = &create_client($server, "127.1.0.2");
++
++&isns_start_server($server);
++
++# 1: Enroll the client1
++&isns_enroll_client($client1);
++
++# 2: Enroll the client1
++&isns_enroll_client($client2, "node-type=target");
++
++&isns_stage("registration", "Registering both clients");
++&__isns_register_client($client1, "initiator portal");
++&__isns_register_client($client2, "target portal");
++&isns_verify_db($server);
++
++# Now each of the two clients should just see
++# itself
++&isns_query_objects($client1, "eid");
++&isns_query_objects($client2, "eid");
++
++# Register a DD linking the two nodes
++&isns_register_domain($client1, "member-name=isns.client1", "member-name=isns.client2");
++
++# Now the clients should see each other
++&isns_query_objects($client1, "eid");
++&isns_query_objects($client2, "eid");
++
++# Initiator querying for target:
++&isns_query_objects($client1, "iscsi-node-type=Target");
++
++# Add another member to this DD, and re-add client2 (making
++# sure the server doesn't generate dupes)
++&isns_register_domain($client1, "dd-id=1", "member-name=isns.client2", "member-name=iqn.com.foobar:disk1");
++
++# Query the list of DDs we're a member of
++&isns_query_objects($client1, "dd-id");
++
++# Remove some entries from the DD
++&isns_deregister_domain($client1, "1", "member-iscsi-idx=10");
++&isns_deregister_domain($client1, "1", "member-name=iqn.com.foobar:disk1");
++&isns_register_domain($client1, "dd-id=1", "member-name=isns.client2");
++&isns_deregister_domain($client1, "1");
++
++&isns_finish;
+diff --git a/utils/open-isns/tests/test03.pl b/utils/open-isns/tests/test03.pl
+new file mode 100644
+index 0000000..3cc0d71
+--- /dev/null
++++ b/utils/open-isns/tests/test03.pl
+@@ -0,0 +1,27 @@
++#!/usr/bin/perl
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This test case validates registration and unregistration.
++
++push(@INC, ".");
++require "harness.pl";
++
++&test_prep("test03", @ARGV);
++
++$server = &create_server;
++$client = &create_client($server);
++
++&isns_start_server($server);
++
++&isns_enroll_client($client);
++&isns_register_client($client, "initiator portal");
++
++# Unregistering the portal should leave the iscsi node and
++# portal group active, and move the portal to state limbo.
++&isns_unregister_client($client, "portal=127.0.0.1:860");
++
++# As the iscsi node goes away, so should the whole entity
++&isns_unregister_client($client, "iscsi-name=isns.client1");
++
++&isns_finish;
+diff --git a/utils/open-isns/tests/test04.pl b/utils/open-isns/tests/test04.pl
+new file mode 100644
+index 0000000..8181a4e
+--- /dev/null
++++ b/utils/open-isns/tests/test04.pl
+@@ -0,0 +1,30 @@
++#!/usr/bin/perl
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This test case verifies that the database remains intact
++# across server restarts.
++
++push(@INC, ".");
++require "harness.pl";
++
++&test_prep("test04", @ARGV);
++
++$server = &create_server;
++$client = &create_client($server);
++
++&isns_start_server($server);
++
++&isns_enroll_client($client);
++&isns_register_client($client, "initiator portal");
++
++# Restart the server, and make sure it still displays
++# the database properly
++&isns_stage("restart", "Restarting server process");
++&isns_restart_server($server);
++&isns_verify_db($server);
++
++# Run a simple query
++&isns_query_objects($client, "iscsi-name");
++
++&isns_finish;
+diff --git a/utils/open-isns/tests/test05.pl b/utils/open-isns/tests/test05.pl
+new file mode 100644
+index 0000000..694d7c3
+--- /dev/null
++++ b/utils/open-isns/tests/test05.pl
+@@ -0,0 +1,25 @@
++#!/usr/bin/perl
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This test case verifies entity expiry
++
++push(@INC, ".");
++require "harness.pl";
++
++&isns_prep_slow_test("test05", 30, @ARGV);
++
++$server = &create_server({ "RegistrationPeriod" => "20s" });
++$client = &create_client($server);
++
++&isns_start_server($server);
++
++&isns_enroll_client($client);
++&isns_register_client($client, "initiator portal");
++
++&isns_stage("expired", "Waiting for registration period to expire (25s)");
++&isns_idle(25);
++&isns_verify_db($server);
++
++&isns_finish;
++
+diff --git a/utils/open-isns/tests/test06.pl b/utils/open-isns/tests/test06.pl
+new file mode 100644
+index 0000000..6b6aa05
+--- /dev/null
++++ b/utils/open-isns/tests/test06.pl
+@@ -0,0 +1,50 @@
++#!/usr/bin/perl
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This test case validates DevAttrReg replace mode.
++
++push(@INC, ".");
++require "harness.pl";
++
++&test_prep("test06", @ARGV);
++
++$server = &create_server;
++$client = &create_client($server);
++
++&isns_start_server($server);
++
++# 1: Enroll the client
++&isns_enroll_client($client);
++
++# 2: Register a simple initiator with one portal
++&isns_register_client($client, "initiator portal");
++
++$eid = &isns_query_eid($client);
++unless ($eid) {
++	&isns_die("Cannot obtain entity ID");
++}
++
++# Now replace the portal with different values
++&isns_register_client($client, "--replace entity=$eid initiator portal=192.168.1.1:iscsi");
++&isns_register_client($client, "--replace entity=$eid initiator portal=192.168.1.2:iscsi");
++
++&isns_register_domain($client, "member-name=isns.client1");
++
++# Replace our registration once more. Now the object index of the
++# initiator should not change, since it's a domain member now.
++&isns_register_client($client, "--replace entity=$eid initiator portal=192.168.1.1:iscsi");
++
++# Make the portal a domain member too. Now even the portal index should stay
++# the same. Note that we do not replace the whole entity now, but just the
++# portal
++&isns_register_domain($client, "dd-id=1 member-addr=192.168.1.1 member-port=860");
++&isns_register_client($client, "--replace --key portal=192.168.1.1:iscsi portal=192.168.1.2:iscsi");
++&isns_register_client($client, "--replace --key portal=192.168.1.2:iscsi portal=192.168.1.1:iscsi");
++
++# Now unregister the whole client, and re-register.
++# Portal and client index should remain the same
++&isns_unregister_client($client, "eid=$eid");
++&isns_register_client($client, "initiator portal=192.168.1.1:iscsi");
++
++&isns_finish;
+diff --git a/utils/open-isns/tests/test07.pl b/utils/open-isns/tests/test07.pl
+new file mode 100644
+index 0000000..c13df11
+--- /dev/null
++++ b/utils/open-isns/tests/test07.pl
+@@ -0,0 +1,37 @@
++#!/usr/bin/perl
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This test case validates that the server discards portals
++# that do not respond to ESI messages
++
++push(@INC, ".");
++require "harness.pl";
++
++&isns_prep_slow_test("test07", 30, @ARGV);
++
++$server = &create_server({ "ESIMinInterval" => "5s" });
++$client = &create_client($server);
++
++&isns_start_server($server);
++
++# 1: Enroll the client
++&isns_enroll_client($client);
++
++# 2: Register a simple initiator with one portal
++&isns_register_client($client, "initiator portal,esi-port=65535,esi-interval=5");
++
++&isns_stage("expired", "Waiting for ESI to expire (~15 sec)");
++&isns_idle(15);
++&isns_verify_db($server);
++
++# 3: Register a simple initiator with two portals, one with ESI and one without.
++# When the ESI monitored portal expires, this should still take down
++# the whole network entity.
++&isns_register_client($client, "initiator portal,esi-port=65535,esi-interval=5 portal=127.0.0.1:1");
++
++&isns_stage("expired", "Waiting for ESI to expire (~15 sec)");
++&isns_idle(15);
++&isns_verify_db($server);
++
++&isns_finish;
+diff --git a/utils/open-isns/tests/test08.pl b/utils/open-isns/tests/test08.pl
+new file mode 100644
+index 0000000..1487532
+--- /dev/null
++++ b/utils/open-isns/tests/test08.pl
+@@ -0,0 +1,23 @@
++#!/usr/bin/perl
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This test case validates registration and simple query of
++# single client.
++
++push(@INC, ".");
++require "harness.pl";
++
++# For now, this one will run w/o security only
++push(@ARGV, '-i');
++
++&test_prep("test08", @ARGV);
++
++$server = &create_server;
++$client = &create_client($server);
++
++&isns_start_server($server);
++
++&isns_external_test($client, "tests/pauw1");
++
++&isns_finish;
+diff --git a/utils/open-isns/tests/test09.pl b/utils/open-isns/tests/test09.pl
+new file mode 100644
+index 0000000..bd2bd7f
+--- /dev/null
++++ b/utils/open-isns/tests/test09.pl
+@@ -0,0 +1,23 @@
++#!/usr/bin/perl
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This test case validates registration and simple query of
++# single client.
++
++push(@INC, ".");
++require "harness.pl";
++
++# For now, this one will run w/o security only
++push(@ARGV, '-i');
++
++&test_prep("test09", @ARGV);
++
++$server = &create_server;
++$client = &create_client($server);
++
++&isns_start_server($server);
++
++&isns_external_test($client, "tests/pauw2");
++
++&isns_finish;
+diff --git a/utils/open-isns/tests/test10.pl b/utils/open-isns/tests/test10.pl
+new file mode 100644
+index 0000000..7286521
+--- /dev/null
++++ b/utils/open-isns/tests/test10.pl
+@@ -0,0 +1,33 @@
++#!/usr/bin/perl
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This test case validates registration and simple query of
++# single client.
++
++push(@INC, ".");
++require "harness.pl";
++
++# For now, this one will run w/o security only
++push(@ARGV, '-i');
++
++&isns_prep_slow_test("test10", 20, @ARGV);
++
++$server = &create_server({ "ESIMinInterval" => "10s" });
++$client = &create_client($server);
++
++&isns_start_server($server);
++
++&isns_external_test($client, "tests/pauw3", "16");
++
++&isns_stage("expired", "Waiting for ESI to come around");
++&isns_idle(5);
++&isns_verify_db($server);
++
++&isns_external_test($client, "tests/pauw3", "-n", "16");
++
++&isns_stage("expired", "Waiting for ESI to come around");
++&isns_idle(5);
++&isns_verify_db($server);
++
++&isns_finish;
+diff --git a/utils/open-isns/tests/test11.pl b/utils/open-isns/tests/test11.pl
+new file mode 100644
+index 0000000..2745955
+--- /dev/null
++++ b/utils/open-isns/tests/test11.pl
+@@ -0,0 +1,23 @@
++#!/usr/bin/perl
++#
++# Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++#
++# This test case validates registration and simple query of
++# single client.
++
++push(@INC, ".");
++require "harness.pl";
++
++# For now, this one will run w/o security only
++push(@ARGV, '-i');
++
++&test_prep("test11", @ARGV);
++
++$server = &create_server;
++$client = &create_client($server);
++
++&isns_start_server($server);
++
++&isns_external_test($client, "tests/pauw4");
++
++&isns_finish;
+diff --git a/utils/open-isns/timer.c b/utils/open-isns/timer.c
+new file mode 100644
+index 0000000..ed8a23f
+--- /dev/null
++++ b/utils/open-isns/timer.c
+@@ -0,0 +1,126 @@
++/*
++ * Timers (one-short and periodic)
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <time.h>
++#include "isns.h"
++#include "util.h"
++
++typedef struct isns_timer isns_timer_t;
++struct isns_timer {
++	isns_list_t		it_list;
++	time_t			it_when;
++	unsigned int		it_period;
++	isns_timer_callback_t *	it_func;
++	void *			it_data;
++};
++
++
++static ISNS_LIST_DECLARE(timers);
++
++static void
++__isns_arm_timer(isns_timer_t *tm)
++{
++	isns_list_t	*pos, *next;
++	time_t		when = tm->it_when;
++
++	isns_list_foreach(&timers, pos, next) {
++		isns_timer_t *cur = isns_list_item(isns_timer_t, it_list, pos);
++
++		if (when < cur->it_when)
++			break;
++	}
++	isns_item_insert_before(pos, &tm->it_list);
++}
++
++static isns_timer_t *
++__isns_create_timer(time_t when,
++		unsigned int period,
++		isns_timer_callback_t *fn,
++		void *data)
++{
++	isns_timer_t	*tm;
++
++	tm = isns_calloc(1, sizeof(*tm));
++	tm->it_when = when;
++	tm->it_period = period;
++	tm->it_func = fn;
++	tm->it_data = data;
++	return tm;
++}
++
++void
++isns_add_timer(unsigned int period,
++		isns_timer_callback_t *fn,
++		void *data)
++{
++	isns_timer_t	*tm;
++
++	isns_assert(period);
++	tm = __isns_create_timer(time(NULL) + period, period, fn, data);
++	__isns_arm_timer(tm);
++}
++
++void
++isns_add_oneshot_timer(unsigned int expires,
++		isns_timer_callback_t *fn,
++		void *data)
++{
++	isns_timer_t	*tm;
++
++	tm = __isns_create_timer(time(NULL) + expires, 0, fn, data);
++	__isns_arm_timer(tm);
++}
++
++void
++isns_cancel_timer(isns_timer_callback_t *fn, void *data)
++{
++	isns_list_t	*pos, *next;
++
++	isns_list_foreach(&timers, pos, next) {
++		isns_timer_t *tm = isns_list_item(isns_timer_t, it_list, pos);
++
++		if (tm->it_func == fn
++		 && (data == NULL || tm->it_data == data)) {
++			isns_list_del(pos);
++			isns_free(tm);
++		}
++	}
++}
++
++time_t
++isns_run_timers(void)
++{
++
++	while (!isns_list_empty(&timers)) {
++		isns_timer_t *tm = isns_list_item(isns_timer_t, it_list, timers.next);
++		isns_timer_callback_t *func;
++		time_t expire;
++		void *data;
++
++		expire = tm->it_when;
++		if (time(NULL) < expire)
++			return expire;
++
++		isns_list_del(&tm->it_list);
++		func = tm->it_func;
++		data = tm->it_data;
++		expire = 0;
++
++		/* If it's a periodic timer, rearm it now. This allows
++		 * the timer callback to cancel the timer. */
++		if (tm->it_period) {
++			tm->it_when = time(NULL) + tm->it_period;
++			__isns_arm_timer(tm);
++		} else {
++			isns_free(tm);
++		}
++
++		func(data);
++	}
++
++	return 0;
++}
+diff --git a/utils/open-isns/types.h b/utils/open-isns/types.h
+new file mode 100644
+index 0000000..ddd153f
+--- /dev/null
++++ b/utils/open-isns/types.h
+@@ -0,0 +1,57 @@
++/*
++ * Open-iSNS types
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_TYPES_H
++#define ISNS_TYPES_H
++
++typedef struct isns_simple	isns_simple_t;
++typedef struct isns_source	isns_source_t;
++typedef struct isns_object	isns_object_t;
++typedef struct isns_relation	isns_relation_t;
++typedef struct isns_attr	isns_attr_t;
++typedef struct isns_attr_list	isns_attr_list_t;
++typedef struct isns_message	isns_message_t;
++typedef struct isns_socket	isns_socket_t;
++typedef struct isns_db		isns_db_t;
++typedef struct isns_tag_type	isns_tag_type_t;
++typedef const struct isns_object_template isns_object_template_t;
++typedef struct isns_authdata	isns_authdata_t;
++typedef struct isns_security	isns_security_t;
++typedef struct isns_principal	isns_principal_t;
++typedef struct isns_policy	isns_policy_t;
++typedef struct isns_keystore	isns_keystore_t;
++typedef struct isns_scope	isns_scope_t;
++typedef struct isns_portal_info isns_portal_info_t;
++typedef struct isns_server	isns_server_t;
++typedef struct isns_db_event	isns_db_event_t;
++typedef struct isns_bitvector	isns_bitvector_t;
++
++typedef struct isns_object_list {
++	unsigned int		iol_count;
++	isns_object_t **	iol_data;
++} isns_object_list_t;
++
++#define ISNS_OBJECT_LIST_INIT	{ .iol_count = 0, .iol_data = NULL }
++
++/*
++ * An attribute list
++ */
++struct isns_attr_list {
++	unsigned int		ial_count;
++	isns_attr_t **		ial_data;
++};
++#define ISNS_ATTR_LIST_INIT	{ .ial_count = 0, .ial_data = NULL }
++
++/*
++ * Function types.
++ */
++typedef void			isns_print_fn_t(const char *, ...);
++typedef void			isns_timer_callback_t(void *);
++
++
++#endif /* ISNS_TYPES_H */
++
++
+diff --git a/utils/open-isns/util.c b/utils/open-isns/util.c
+new file mode 100644
+index 0000000..4c0a7b2
+--- /dev/null
++++ b/utils/open-isns/util.c
+@@ -0,0 +1,263 @@
++/*
++ * util.c
++ *
++ * Misc utility functions
++ *
++ * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <sys/stat.h>
++#include <stdlib.h>
++#include <string.h>
++#include <err.h>
++#include <errno.h>
++#include "util.h"
++
++unsigned long
++parse_size(const char *arg)
++{
++    unsigned long	mult = 1, ret;
++    char		*s;
++
++    ret = strtol(arg, &s, 0);
++
++    switch (*s++) {
++    case 'g':
++    case 'G':
++        mult = 1024 * 1024 * 1024;
++	break;
++    case 'm':
++    case 'M':
++        mult = 1024 * 1024;
++	break;
++    case 'k':
++    case 'K':
++        mult = 1024;
++	break;
++
++    case '\0':
++	return ret;
++
++    default:
++    bad:
++	err(1, "parse_size: unknown unit in \"%s\"\n", arg);
++    }
++
++    if (*s != '\0')
++	    goto bad;
++
++    return mult * ret;
++}
++
++char *
++print_size(unsigned long size)
++{
++	static char	unit[] = "-kMG";
++	static char	buffer[64];
++	unsigned int	power = 0;
++
++	while (size && !(size % 1024) && power < sizeof(unit)) {
++		size /= 1024;
++		power++;
++	}
++
++	if (!power) {
++		snprintf(buffer, sizeof(buffer), "%lu", size);
++	} else {
++		snprintf(buffer, sizeof(buffer), "%lu%c",
++				size, unit[power]);
++	}
++	return buffer;
++}
++
++unsigned int
++parse_count(const char *arg)
++{
++    unsigned long	ret;
++    char		*s;
++
++    ret = strtoul(arg, &s, 0);
++    if (*s != '\0')
++	err(1, "parse_count: unexpected character in \"%s\"\n", arg);
++
++    return ret;
++}
++
++int
++parse_int(const char *arg)
++{
++    long	ret;
++    char	*s;
++
++    ret = strtol(arg, &s, 0);
++    if (*s != '\0')
++	err(1, "parse_count: unexpected character in \"%s\"\n", arg);
++
++    return ret;
++}
++
++long long
++parse_longlong(const char *arg)
++{
++    long long	ret;
++    char	*s;
++
++    ret = strtoll(arg, &s, 0);
++    if (*s != '\0')
++	err(1, "parse_count: unexpected character in \"%s\"\n", arg);
++
++    return ret;
++}
++
++double
++parse_double(const char *arg)
++{
++	double	ret;
++	char	*s;
++
++	ret = strtod(arg, &s);
++	if (*s != '\0')
++		err(1, "parse_count: unexpected character in \"%s\"\n", arg);
++
++	return ret;
++}
++
++unsigned int
++parse_timeout(const char *arg)
++{
++	unsigned int	v, ret = 0;
++	char		*s;
++
++	do {
++		v = strtoul(arg, &s, 10);
++		switch (*s) {
++		case '\0':
++			ret += v;
++			break;
++		case 'd':
++			v *= 24;
++		case 'h':
++			v *= 60;
++		case 'm':
++			v *= 60;
++		case 's':
++			ret += v;
++			++s;
++			break;
++
++		default:
++			errx(1, "parse_timeout: unexpected character in \"%s\"\n",
++					arg);
++		}
++
++		arg = s;
++	} while (*arg);
++
++	return ret;
++}
++
++void
++isns_string_array_append(struct string_array *array, const char *val)
++{
++	if (!(array->count % 32)) {
++		array->list = isns_realloc(array->list,
++				(array->count + 32) * sizeof(val));
++	}
++	array->list[array->count++] = val? isns_strdup(val) : NULL;
++}
++
++void
++isns_string_array_destroy(struct string_array *array)
++{
++	unsigned int	i;
++
++	for (i = 0; i < array->count; ++i)
++		isns_free(array->list[i]);
++	isns_free(array->list);
++	memset(array, 0, sizeof(*array));
++}
++
++void
++isns_assign_string(char **var, const char *val)
++{
++	char	*s = NULL;
++
++	if (val && !(s = isns_strdup(val)))
++		errx(1, "out of memory");
++
++	if (*var)
++		isns_free(*var);
++	*var = s;
++}
++
++/*
++ * Recursively create a directory
++ */
++int
++isns_mkdir_recursive(const char *pathname)
++{
++	const char *orig_pathname = pathname;
++	char	*squirrel[64];
++	char	*copy = NULL, *s;
++	int	ns = 0;
++
++	if (!pathname || !strcmp(pathname, "."))
++		return 0;
++	while (1) {
++		if (mkdir(pathname, 0755) >= 0) {
++			if (ns == 0)
++				break;
++			*squirrel[--ns] = '/';
++			continue;
++		}
++
++		if (errno == EEXIST)
++			goto good;
++		if (errno != ENOENT)
++			goto bad;
++
++		if (copy == NULL) {
++			copy = isns_strdup(pathname);
++			pathname = copy;
++		}
++
++		s = strrchr(copy, '/');
++		while (s > copy && s[-1] == '/')
++			--s;
++		*s = '\0';
++
++		isns_assert(ns < 64);
++		squirrel[ns++] = s;
++
++		if (s == copy)
++			goto bad;
++	}
++
++good:	if (copy)
++		isns_free(copy);
++	errno = 0;
++	return 0;
++
++bad:	if (copy)
++		isns_free(copy);
++	perror(orig_pathname);
++	return -1;
++}
++
++/*
++ * This one differs from POSIX dirname; it does not
++ * modify its argument
++ */
++const char *
++isns_dirname(const char *pathname)
++{
++	static char	buffer[4096];
++	char		*s;
++
++	strcpy(buffer, pathname);
++	if ((s = strrchr(buffer, '/')) != NULL) {
++		*s = '\0';
++		return buffer;
++	}
++	return ".";
++}
+diff --git a/utils/open-isns/util.h b/utils/open-isns/util.h
+new file mode 100644
+index 0000000..deecb24
+--- /dev/null
++++ b/utils/open-isns/util.h
+@@ -0,0 +1,289 @@
++/*
++ * Utility functions
++ *
++ * Copyright (C) 2006, 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef UTIL_H
++#define UTIL_H
++
++#include <sys/types.h>
++#include <stdint.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <stddef.h>
++#include <string.h>	// for strdup
++#include "types.h"
++
++#define array_num_elements(a) (sizeof(a) / sizeof((a)[0]))
++
++const char *	isns_dirname(const char *);
++int		isns_mkdir_recursive(const char *);
++
++extern const char *parser_separators;
++char *		parser_get_next_line(FILE *);
++char *		parser_get_next_word(char **);
++char *		parser_get_rest_of_line(char **);
++int		parser_split_line(char *, unsigned int, char **);
++
++unsigned long	parse_size(const char *);
++unsigned int	parse_count(const char *);
++int		parse_int(const char *);
++long long	parse_longlong(const char *);
++double		parse_double(const char *);
++unsigned int	parse_timeout(const char *);
++
++char *		print_size(unsigned long);
++
++/*
++ * Very simple and stupid string array.
++ */
++struct string_array {
++	unsigned int	count;
++	char **		list;
++};
++
++void		isns_string_array_append(struct string_array *, const char *);
++void		isns_string_array_destroy(struct string_array *);
++
++void		isns_assign_string(char **, const char *);
++
++void		isns_write_pidfile(const char *);
++void		isns_update_pidfile(const char *);
++void		isns_remove_pidfile(const char *);
++
++extern void	isns_log_background(void);
++extern void	isns_assert_failed(const char *,
++			const char *, unsigned int);
++extern void	isns_fatal(const char *, ...);
++extern void	isns_warning(const char *, ...);
++extern void	isns_error(const char *, ...);
++extern void	isns_notice(const char *, ...);
++extern void	isns_debug_general(const char *, ...);
++extern void	isns_debug_socket(const char *, ...);
++extern void	isns_debug_protocol(const char *, ...);
++extern void	isns_debug_message(const char *, ...);
++extern void	isns_debug_state(const char *, ...);
++extern void	isns_debug_auth(const char *, ...);
++extern void	isns_debug_scn(const char *, ...);
++extern void	isns_debug_esi(const char *, ...);
++extern void	isns_enable_debugging(const char *);
++extern int	isns_debug_enabled(int);
++
++enum {
++	DBG_GENERAL = 0,
++	DBG_SOCKET,
++	DBG_PROTOCOL,
++	DBG_MESSAGE,
++	DBG_STATE,
++	DBG_AUTH,
++	DBG_SCN,
++	DBG_ESI,
++};
++
++/*
++ * There's no htonll yet
++ */
++#ifndef htonll
++# include <endian.h>
++# include <byteswap.h>
++# if __BYTE_ORDER == __BIG_ENDIAN
++#  define htonll(x)	(x)
++#  define ntohll(x)	(x)
++# elif __BYTE_ORDER == __LITTLE_ENDIAN
++#  define htonll(x)	__bswap_64(x)
++#  define ntohll(x)	__bswap_64(x)
++# endif
++#endif
++
++/*
++ * One of the those eternal staples of C coding:
++ */
++#ifndef MIN
++# define MIN(a, b)	((a) < (b)? (a) : (b))
++# define MAX(a, b)	((a) > (b)? (a) : (b))
++#endif
++
++#define DECLARE_BITMAP(name, NBITS) \
++	uint32_t	name[(NBITS+31) >> 5] = { 0 }
++
++#define __BIT_INDEX(nr)	(nr >> 5)
++#define __BIT_MASK(nr)	(1 << (nr & 31))
++
++static inline void
++set_bit(uint32_t *map, unsigned int nr)
++{
++	map[__BIT_INDEX(nr)] |= __BIT_MASK(nr);
++}
++
++static inline void
++clear_bit(uint32_t *map, unsigned int nr)
++{
++	map[__BIT_INDEX(nr)] &= ~__BIT_MASK(nr);
++}
++
++static inline int
++test_bit(const uint32_t *map, unsigned int nr)
++{
++	return !!(map[__BIT_INDEX(nr)] & __BIT_MASK(nr));
++}
++
++/*
++ * Dynamically sized bit vector
++ */
++extern isns_bitvector_t *isns_bitvector_alloc(void);
++extern void	isns_bitvector_init(isns_bitvector_t *);
++extern void	isns_bitvector_destroy(isns_bitvector_t *);
++extern void	isns_bitvector_free(isns_bitvector_t *);
++extern int	isns_bitvector_test_bit(const isns_bitvector_t *, unsigned int);
++extern int	isns_bitvector_set_bit(isns_bitvector_t *, unsigned int);
++extern int	isns_bitvector_clear_bit(isns_bitvector_t *, unsigned int);
++extern int	isns_bitvector_is_empty(const isns_bitvector_t *);
++extern int	isns_bitvector_intersect(const isns_bitvector_t *a,
++				const isns_bitvector_t *b,
++				isns_bitvector_t *result);
++extern void	isns_bitvector_print(const isns_bitvector_t *,
++				isns_print_fn_t *);
++extern void	isns_bitvector_foreach(const isns_bitvector_t *bv,
++				int (*cb)(uint32_t, void *),
++				void *user_data);
++
++/*
++ * List manipulation primites
++ */
++typedef struct isns_list isns_list_t;
++struct isns_list {
++	isns_list_t *	next;
++	isns_list_t *	prev;
++};
++
++#define ISNS_LIST_DECLARE(list) \
++	isns_list_t list = { &list, &list }
++
++static inline void
++isns_list_init(isns_list_t *head)
++{
++	head->next = head->prev = head;
++}
++
++static inline void
++__isns_list_insert(isns_list_t *prev, isns_list_t *item, isns_list_t *next)
++{
++	item->next = next;
++	item->prev = prev;
++	next->prev = item;
++	prev->next = item;
++}
++
++static inline void
++isns_list_append(isns_list_t *head, isns_list_t *item)
++{
++	__isns_list_insert(head->prev, item, head);
++}
++
++static inline void
++isns_list_insert(isns_list_t *head, isns_list_t *item)
++{
++	__isns_list_insert(head, item, head->next);
++}
++
++static inline void
++isns_item_insert_before(isns_list_t *where, isns_list_t *item)
++{
++	__isns_list_insert(where->prev, item, where);
++}
++
++static inline void
++isns_item_insert_after(isns_list_t *where, isns_list_t *item)
++{
++	__isns_list_insert(where, item, where->next);
++}
++
++static inline void
++isns_list_del(isns_list_t *item)
++{
++	isns_list_t	*prev = item->prev;
++	isns_list_t	*next = item->next;
++
++	prev->next = next;
++	next->prev = prev;
++	item->next = item->prev = item;
++}
++
++static inline int
++isns_list_empty(const isns_list_t *head)
++{
++	return head == head->next;
++}
++
++static inline void
++isns_list_move(isns_list_t *dst, isns_list_t *src)
++{
++	isns_list_t	*prev, *next;
++	isns_list_t	*head, *tail;
++
++	if (isns_list_empty(src))
++		return;
++
++	prev = dst->prev;
++	next = dst;
++
++	head = src->next;
++	tail = src->prev;
++
++	next->prev = tail;
++	prev->next = head;
++	head->prev = prev;
++	tail->next = next;
++
++	src->next = src->prev = src;
++}
++
++#define isns_list_item(type, member, ptr) \
++	container_of(type, member, ptr)
++
++#define isns_list_foreach(list, __pos, __next) \
++	for (__pos = (list)->next; \
++	     (__pos != list) && (__next = __pos->next, 1); \
++	     __pos = __next) 
++
++#if 0
++/* This is defined in stddef */
++#define offsetof(type, member)		((unsigned long) &(((type *) 0)->member))
++#endif
++#define container_of(type, member, ptr) \
++	((type *) (((unsigned char *) ptr) - offsetof(type, member)))
++
++/*
++ * Use isns_assert instead of libc's assert, so that the
++ * message can be captured and sent to syslog.
++ */
++#define isns_assert(condition) do { \
++	if (!(condition))			\
++		isns_assert_failed(#condition,	\
++			__FILE__, __LINE__);	\
++} while (0)
++
++#ifndef MDEBUG
++# define isns_malloc(size)		malloc(size)
++# define isns_calloc(n, size)		calloc(n, size)
++# define isns_realloc(p, size)		realloc(p, size)
++# define isns_strdup(s)			strdup(s)
++# define isns_free(p)			free(p)
++#else
++# define isns_malloc(size)		isns_malloc_fn(size, __FILE__, __LINE__)
++# define isns_calloc(n, size)		isns_calloc_fn(n, size, __FILE__, __LINE__)
++# define isns_realloc(p, size)		isns_realloc_fn(p, size, __FILE__, __LINE__)
++# define isns_strdup(s)			isns_strdup_fn(s, __FILE__, __LINE__)
++# define isns_free(p)			isns_free_fn(p, __FILE__, __LINE__)
++
++extern void *		(*isns_malloc_fn)(size_t, const char *, unsigned int);
++extern void *		(*isns_calloc_fn)(unsigned int, size_t,
++				const char *, unsigned int);
++extern void *		(*isns_realloc_fn)(void *, size_t,
++				const char *, unsigned int);
++extern char *		(*isns_strdup_fn)(const char *, const char *, unsigned int);
++extern void		(*isns_free_fn)(void *, const char *, unsigned int);
++#endif
++
++#endif /* UTIL_H */
+diff --git a/utils/open-isns/vendor.c b/utils/open-isns/vendor.c
+new file mode 100644
+index 0000000..e24164d
+--- /dev/null
++++ b/utils/open-isns/vendor.c
+@@ -0,0 +1,41 @@
++/*
++ * iSNS vendor specific objects
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include "isns.h"
++#include "objects.h"
++#include "attrs.h"
++#include "vendor.h"
++#include "util.h"
++
++static uint32_t policy_attrs[] = {
++	OPENISNS_TAG_POLICY_SPI,
++	OPENISNS_TAG_POLICY_KEY,
++	OPENISNS_TAG_POLICY_ENTITY,
++	OPENISNS_TAG_POLICY_OBJECT_TYPE,
++	OPENISNS_TAG_POLICY_NODE_NAME,
++	OPENISNS_TAG_POLICY_NODE_TYPE,
++	OPENISNS_TAG_POLICY_FUNCTIONS,
++	OPENISNS_TAG_POLICY_VISIBLE_DD,
++	OPENISNS_TAG_POLICY_DEFAULT_DD,
++};
++
++static uint32_t policy_key_attrs[] = {
++	OPENISNS_TAG_POLICY_SPI,
++};
++
++isns_object_template_t		isns_policy_template = {
++	.iot_name	= "Policy",
++	.iot_handle	= ISNS_OBJECT_TYPE_POLICY,
++	.iot_attrs	= policy_attrs,
++	.iot_num_attrs	= array_num_elements(policy_attrs),
++	.iot_keys	= policy_key_attrs,
++	.iot_num_keys	= array_num_elements(policy_key_attrs),
++	.iot_container	= &isns_entity_template,
++	.iot_vendor_specific = 1,
++};
++
+diff --git a/utils/open-isns/vendor.h b/utils/open-isns/vendor.h
+new file mode 100644
+index 0000000..49c6132
+--- /dev/null
++++ b/utils/open-isns/vendor.h
+@@ -0,0 +1,56 @@
++/*
++ * iSNS "vendor-specific" protocol definitions
++ *
++ * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
++ */
++
++#ifndef ISNS_VENDOR_H
++#define ISNS_VENDOR_H
++
++#include "isns-proto.h"
++
++/*
++ * We're poor, we don't own a OUI. Let's fake one.
++ */
++#define OPENISNS_VENDOR_OUI	0xFFFF00
++#define OPENISNS_VENDOR_PREFIX	(OPENISNS_VENDOR_OUI << 8)
++#define OPENISNS_IS_PRIVATE_ATTR(tag) (((tag) >> 16) == 0xFFFF)
++
++enum openisns_vendor_tag {
++	/* Security Policy Identifier */
++	OPENISNS_TAG_POLICY_SPI	= OPENISNS_VENDOR_PREFIX + ISNS_VENDOR_SPECIFIC_OTHER_BASE,
++
++	__OPENISNS_TAG_POLICY_RESERVED,
++
++	/* DSA signature key (public) */
++	OPENISNS_TAG_POLICY_KEY,
++
++	/* Entity name to use */
++	OPENISNS_TAG_POLICY_ENTITY,
++
++	/* Functions the client is permitted to invoke */
++	OPENISNS_TAG_POLICY_FUNCTIONS,
++
++	/* Object types the client is permitted to see. */
++	OPENISNS_TAG_POLICY_OBJECT_TYPE,
++
++	/* iSCSI node name the client is permitted to register.
++	 * This attribute may occur multiple times.
++	 * If absent, it defaults to POLICY_SOURCE_NAME
++	 */
++	OPENISNS_TAG_POLICY_NODE_NAME,
++
++	/* Node type bitmap the client is permitted to register */
++	OPENISNS_TAG_POLICY_NODE_TYPE,
++
++	/* Default discovery domain the client will be
++	 * placed in.
++	 * Not used yet.
++	 */
++	OPENISNS_TAG_POLICY_DEFAULT_DD,
++	OPENISNS_TAG_POLICY_VISIBLE_DD,
++};
++
++extern const struct isns_object_template	isns_policy_template;
++
++#endif /* ISNS_VENDOR_H */
+-- 
+2.5.5
+
diff --git a/SOURCES/open-iscsi-2.0.874-1-iBFT-origin-is-an-enum-not-a-string.patch b/SOURCES/open-iscsi-2.0.874-1-iBFT-origin-is-an-enum-not-a-string.patch
new file mode 100644
index 0000000..b688f2c
--- /dev/null
+++ b/SOURCES/open-iscsi-2.0.874-1-iBFT-origin-is-an-enum-not-a-string.patch
@@ -0,0 +1,29 @@
+From 3f15a2270a7efb1a6ee8ef555b01f3d8674818b9 Mon Sep 17 00:00:00 2001
+From: Lee Duncan <leeman.duncan@gmail.com>
+Date: Fri, 10 Jul 2015 11:58:55 -0700
+Subject: iBFT 'origin' is an enum, not a string
+
+A recent change, commit 4959a89f421fdebc, modified open-iscsi
+to treat the "origin" field as an enum, not a character
+string. But one spot was missed.
+---
+ utils/fwparam_ibft/fwparam_ibft_sysfs.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/utils/fwparam_ibft/fwparam_ibft_sysfs.c b/utils/fwparam_ibft/fwparam_ibft_sysfs.c
+index 2dc6f6d5fe54..019fc19184bb 100644
+--- a/utils/fwparam_ibft/fwparam_ibft_sysfs.c
++++ b/utils/fwparam_ibft/fwparam_ibft_sysfs.c
+@@ -201,8 +201,7 @@ static int fill_nic_context(char *id, struct boot_context *context)
+ 		      sizeof(context->secondary_dns));
+ 	sysfs_get_str(id, IBFT_SUBSYS, "dhcp", context->dhcp,
+ 		      sizeof(context->dhcp));
+-	sysfs_get_str(id, IBFT_SUBSYS, "origin", context->origin,
+-		      sizeof(context->origin));
++	sysfd_get_int(id, IBFT_SUBSYS, "origin", &context->origin);
+ 	return 0;
+ }
+ 
+-- 
+2.9.3
+
diff --git a/SOURCES/open-iscsi-2.0.874-23-Fix-manual-LUN-scans-feature.patch b/SOURCES/open-iscsi-2.0.874-23-Fix-manual-LUN-scans-feature.patch
new file mode 100644
index 0000000..b4b7aa6
--- /dev/null
+++ b/SOURCES/open-iscsi-2.0.874-23-Fix-manual-LUN-scans-feature.patch
@@ -0,0 +1,62 @@
+From d5483b0df96bd2a1cf86039cf4c6822ec7d7f609 Mon Sep 17 00:00:00 2001
+From: Gorka Eguileor <geguileo@redhat.com>
+Date: Fri, 17 Mar 2017 16:00:11 +0100
+Subject: Fix manual LUN scans feature
+
+The newly introduced feature to disable automatic scans should not be
+scanning *any* of the LUNs when the scan is set to manual, but it always
+scans for LUN0.
+
+This patch fixes this by skipping the sysfs call altogether, as it
+should have been doing from the start.
+---
+ usr/iscsi_sysfs.c | 13 +++++++------
+ usr/iscsi_sysfs.h |  2 +-
+ 2 files changed, 8 insertions(+), 7 deletions(-)
+
+diff --git a/usr/iscsi_sysfs.c b/usr/iscsi_sysfs.c
+index 2f94b632baaa..0cc55b97bde9 100644
+--- a/usr/iscsi_sysfs.c
++++ b/usr/iscsi_sysfs.c
+@@ -1883,18 +1883,19 @@ void iscsi_sysfs_rescan_device(void *data, int hostno, int target, int lun)
+ 			strlen(write_buf));
+ }
+ 
+-pid_t iscsi_sysfs_scan_host(int hostno, int async, int full_scan)
++pid_t iscsi_sysfs_scan_host(int hostno, int async, int autoscan)
+ {
+ 	char id[NAME_SIZE];
+-	char write_buf[6] = "- - 0";
++	char *write_buf = "- - -";
+ 	pid_t pid = 0;
+ 
+-	if (full_scan)
+-		write_buf[4] = '-';
+-
+ 	if (async)
+ 		pid = fork();
+-	if (pid == 0) {
++
++	if (pid >= 0 && !autoscan) {
++		if (pid)
++			log_debug(4, "host%d in manual scan mode, skipping scan", hostno);
++	} else if (pid == 0) {
+ 		/* child */
+ 		log_debug(4, "scanning host%d", hostno);
+ 
+diff --git a/usr/iscsi_sysfs.h b/usr/iscsi_sysfs.h
+index 3492ce6e033c..cdcefa65f683 100644
+--- a/usr/iscsi_sysfs.h
++++ b/usr/iscsi_sysfs.h
+@@ -87,7 +87,7 @@ extern void iscsi_sysfs_get_negotiated_session_conf(int sid,
+ 				struct iscsi_session_operational_config *conf);
+ extern void iscsi_sysfs_get_negotiated_conn_conf(int sid,
+ 				struct iscsi_conn_operational_config *conf);
+-extern pid_t iscsi_sysfs_scan_host(int hostno, int async, int full);
++extern pid_t iscsi_sysfs_scan_host(int hostno, int async, int autoscan);
+ extern int iscsi_sysfs_get_session_state(char *state, int sid);
+ extern int iscsi_sysfs_get_host_state(char *state, int host_no);
+ extern int iscsi_sysfs_get_device_state(char *state, int host_no, int target,
+-- 
+2.9.3
+
diff --git a/SOURCES/open-iscsi-2.0.874-27-iscsid-Add-qedi-ping-transport-hook.patch b/SOURCES/open-iscsi-2.0.874-27-iscsid-Add-qedi-ping-transport-hook.patch
new file mode 100644
index 0000000..617db22
--- /dev/null
+++ b/SOURCES/open-iscsi-2.0.874-27-iscsid-Add-qedi-ping-transport-hook.patch
@@ -0,0 +1,28 @@
+From f910837dace250418cba4155a6708d47d45075cc Mon Sep 17 00:00:00 2001
+From: Nilesh Javili <nilesh.javali@cavium.com>
+Date: Thu, 27 Apr 2017 17:59:00 -0700
+Subject: iscsid: Add qedi ping transport hook
+
+iscsiuio ping is operational for qedi.
+Add missing qedi transport hook for ping support.
+
+Signed-off-by: Nilesh Javali <nilesh.javali@cavium.com>
+---
+ usr/transport.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/usr/transport.c b/usr/transport.c
+index 533ba30a8846..3b7a00a2245e 100644
+--- a/usr/transport.c
++++ b/usr/transport.c
+@@ -124,6 +124,7 @@ struct iscsi_transport_template qedi = {
+ 	.ep_poll	= ktransport_ep_poll,
+ 	.ep_disconnect	= ktransport_ep_disconnect,
+ 	.set_net_config = uip_broadcast_params,
++	.exec_ping	= uip_broadcast_ping_req,
+ };
+ 
+ static struct iscsi_transport_template *iscsi_transport_templates[] = {
+-- 
+2.9.3
+
diff --git a/SOURCES/open-iscsi-2.0.874-30-isolate-iscsistart-socket-use.patch b/SOURCES/open-iscsi-2.0.874-30-isolate-iscsistart-socket-use.patch
new file mode 100644
index 0000000..d5a73db
--- /dev/null
+++ b/SOURCES/open-iscsi-2.0.874-30-isolate-iscsistart-socket-use.patch
@@ -0,0 +1,111 @@
+From 2a463c04a75726e811161f435e2f6736d70a66bd Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Tue, 20 Jun 2017 09:35:23 -0700
+Subject: isolate iscsistart socket use
+
+---
+ usr/iscsid_req.c | 12 +++++++++++-
+ usr/iscsid_req.h |  3 +++
+ usr/iscsistart.c |  6 ++++++
+ usr/mgmt_ipc.c   |  3 ++-
+ usr/mgmt_ipc.h   |  1 -
+ 5 files changed, 22 insertions(+), 3 deletions(-)
+
+diff --git a/usr/iscsid_req.c b/usr/iscsid_req.c
+index 2950d748c644..69351c6c61ea 100644
+--- a/usr/iscsid_req.c
++++ b/usr/iscsid_req.c
+@@ -96,9 +96,19 @@ static int ipc_connect(int *fd, char *unix_sock_name, int start_iscsid)
+ 	return ISCSI_ERR_ISCSID_NOTCONN;
+ }
+ 
++char iscsid_namespace[64] = ISCSIADM_NAMESPACE;
++
++void iscsid_set_namespace(pid_t pid) {
++	if (pid) {
++		snprintf(iscsid_namespace, 64, ISCSIADM_NAMESPACE "-%d", pid);
++	} else {
++		snprintf(iscsid_namespace, 64, ISCSIADM_NAMESPACE);
++	}
++}
++
+ static int iscsid_connect(int *fd, int start_iscsid)
+ {
+-	return ipc_connect(fd, ISCSIADM_NAMESPACE, start_iscsid);
++	return ipc_connect(fd, iscsid_namespace, start_iscsid);
+ }
+ 
+ int iscsid_request(int *fd, iscsiadm_req_t *req, int start_iscsid)
+diff --git a/usr/iscsid_req.h b/usr/iscsid_req.h
+index 8cb4a922f46d..ba7b2357c729 100644
+--- a/usr/iscsid_req.h
++++ b/usr/iscsid_req.h
+@@ -25,6 +25,9 @@ struct iscsiadm_req;
+ struct iscsiadm_rsp;
+ struct node_rec;
+ 
++extern char iscsid_namespace[64];
++extern void iscsid_set_namespace(pid_t);
++
+ extern int iscsid_exec_req(struct iscsiadm_req *req, struct iscsiadm_rsp *rsp,
+ 			   int iscsid_start);
+ extern int iscsid_req_wait(int cmd, int fd);
+diff --git a/usr/iscsistart.c b/usr/iscsistart.c
+index ecb256b3c2f6..538dd2f9d780 100644
+--- a/usr/iscsistart.c
++++ b/usr/iscsistart.c
+@@ -458,6 +458,10 @@ int main(int argc, char *argv[])
+ 	} else if (pid) {
+ 		int status, rc, rc2;
+ 
++		/* make a special socket path for only this iscsistart instance */
++		iscsid_set_namespace(pid);
++		sleep(1);
++
+ 		rc = setup_session();
+ 		rc2 = stop_event_loop();
+ 		/*
+@@ -475,6 +478,9 @@ int main(int argc, char *argv[])
+ 		exit(0);
+ 	}
+ 
++	pid = getpid();
++	iscsid_set_namespace(pid);
++
+ 	mgmt_ipc_fd = mgmt_ipc_listen();
+ 	if (mgmt_ipc_fd  < 0) {
+ 		log_error("Could not setup mgmt ipc");
+diff --git a/usr/mgmt_ipc.c b/usr/mgmt_ipc.c
+index c16bce962232..140d358ec5da 100644
+--- a/usr/mgmt_ipc.c
++++ b/usr/mgmt_ipc.c
+@@ -37,6 +37,7 @@
+ #include "iscsi_ipc.h"
+ #include "iscsi_err.h"
+ #include "iscsi_util.h"
++#include "iscsid_req.h"
+ 
+ #define PEERUSER_MAX	64
+ #define EXTMSG_MAX	(64 * 1024)
+@@ -60,7 +61,7 @@ mgmt_ipc_listen(void)
+ 		return fd;
+ 	}
+ 
+-	addr_len = setup_abstract_addr(&addr, ISCSIADM_NAMESPACE);
++	addr_len = setup_abstract_addr(&addr, iscsid_namespace);
+ 
+ 	if ((err = bind(fd, (struct sockaddr *) &addr, addr_len)) < 0 ) {
+ 		log_error("Can not bind IPC socket");
+diff --git a/usr/mgmt_ipc.h b/usr/mgmt_ipc.h
+index 55972ed793a1..b6b836fc07c1 100644
+--- a/usr/mgmt_ipc.h
++++ b/usr/mgmt_ipc.h
+@@ -115,5 +115,4 @@ int mgmt_ipc_listen(void);
+ int mgmt_ipc_systemd(void);
+ void mgmt_ipc_close(int fd);
+ void mgmt_ipc_handle(int accept_fd);
+-
+ #endif /* MGMT_IPC_H */
+-- 
+2.9.4
+
diff --git a/SOURCES/open-iscsi-2.0.874-4-iscsid-treat-SIGTERM-like-iscsiadm-k-0.patch b/SOURCES/open-iscsi-2.0.874-4-iscsid-treat-SIGTERM-like-iscsiadm-k-0.patch
new file mode 100644
index 0000000..de2661e
--- /dev/null
+++ b/SOURCES/open-iscsi-2.0.874-4-iscsid-treat-SIGTERM-like-iscsiadm-k-0.patch
@@ -0,0 +1,35 @@
+From c3122e9aedc4ebb49090df86e6f53806fed6cebc Mon Sep 17 00:00:00 2001
+From: Lee Duncan <lduncan@suse.com>
+Date: Wed, 23 Nov 2016 14:50:35 -0800
+Subject: iscsid: treat SIGTERM like "iscsiadm -k 0"
+
+The same code that is executed by iscsid
+when iscsiadm sends the "immediate stop"
+command should be executed when iscsid
+receives a SIGTERM.
+
+Changes since v1:
+ * now just set the "event loop stop" flag
+
+Signed-off-by: Lee Duncan <lduncan@suse.com>
+---
+ usr/iscsid.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/usr/iscsid.c b/usr/iscsid.c
+index 0c2634448d09..81a14f259b5f 100644
+--- a/usr/iscsid.c
++++ b/usr/iscsid.c
+@@ -313,8 +313,7 @@ static void catch_signal(int signo)
+ 
+ 	switch (signo) {
+ 	case SIGTERM:
+-		iscsid_shutdown();
+-		exit(0);
++		event_loop_exit(NULL);
+ 		break;
+ 	default:
+ 		break;
+-- 
+2.9.3
+
diff --git a/SOURCES/open-iscsi-2.0.874-5-Make-event_loop_stop-volatile-for-safer-access.patch b/SOURCES/open-iscsi-2.0.874-5-Make-event_loop_stop-volatile-for-safer-access.patch
new file mode 100644
index 0000000..45ed088
--- /dev/null
+++ b/SOURCES/open-iscsi-2.0.874-5-Make-event_loop_stop-volatile-for-safer-access.patch
@@ -0,0 +1,33 @@
+From 3951c242d266689a8c856717b02b07c1fe193a31 Mon Sep 17 00:00:00 2001
+From: Lee Duncan <lduncan@suse.com>
+Date: Fri, 2 Dec 2016 09:22:02 -0800
+Subject: Make event_loop_stop volatile for safer access
+
+As suggested by Christian Seiler:
+"Only minor thing is that you might want to mark
+static int event_loop_stop; (usr/event_poll.c)
+to be volatile, to be on the safe side when modifying it
+from within a signal handler. Probably not really required
+here (the compiler is not allowed to optimize out the
+access anyway, since you call non-static functions within
+the loop), but it doesn't hurt either, just in case... "
+---
+ usr/event_poll.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/usr/event_poll.c b/usr/event_poll.c
+index 209ee0280c0a..ac2504404366 100644
+--- a/usr/event_poll.c
++++ b/usr/event_poll.c
+@@ -123,7 +123,7 @@ static int shutdown_wait_pids(void)
+ #define POLL_ALARM	2
+ #define POLL_MAX	3
+ 
+-static int event_loop_stop;
++static volatile int event_loop_stop;
+ static queue_task_t *shutdown_qtask; 
+ 
+ void event_loop_exit(queue_task_t *qtask)
+-- 
+2.9.3
+
diff --git a/SOURCES/open-iscsi-2.0.874-7-Allow-disabling-auto-LUN-scans.patch b/SOURCES/open-iscsi-2.0.874-7-Allow-disabling-auto-LUN-scans.patch
new file mode 100644
index 0000000..2293021
--- /dev/null
+++ b/SOURCES/open-iscsi-2.0.874-7-Allow-disabling-auto-LUN-scans.patch
@@ -0,0 +1,265 @@
+From 5e32aea95741a07d53153c658a0572588eae494d Mon Sep 17 00:00:00 2001
+From: Gorka Eguileor <geguileo@redhat.com>
+Date: Tue, 21 Feb 2017 20:01:39 +0100
+Subject: Allow disabling auto LUN scans
+
+Existing behavior of auto scanning LUNs is problematic for some
+deployments, particularly in cases where we are:
+
+- Accessing different LUNs from the same target on different machines
+  and we don't want the other LUNs automatically exposed in other
+  systems.
+
+- LUNs are constantly being created and removed from the target by
+  another machine and we don't want our systems polluted by no longer
+  available logical units, since default udev rules don't remove them
+  automatically from the system once they have been added automatically.
+
+This is a little more problematic when working with multipaths as we end
+up with a lot of leftover device maps.
+
+This patch introduces a new configuration option at the session level
+called "scan", with "auto" and "manual" as acceptable values, that
+allows us to disable the autoscan in the following cases:
+
+- On iscsid start
+- On login
+- On AEN/AER messages reporting LUNs data has changed.
+
+For HW drivers all sessions will use the value defined in the
+configuration file.
+
+Default value for this new option is "auto" to maintain existing
+behavior.
+---
+ etc/iscsid.conf      |  8 ++++++++
+ usr/config.h         |  1 +
+ usr/idbm.c           | 14 +++++++++++++-
+ usr/idbm.h           |  1 +
+ usr/idbm_fields.h    |  1 +
+ usr/initiator.c      |  5 +++--
+ usr/iscsi_settings.h |  3 +++
+ usr/iscsi_sysfs.c    |  7 +++++--
+ usr/iscsi_sysfs.h    |  2 +-
+ usr/iscsiadm.c       |  2 +-
+ usr/iscsid.c         |  2 +-
+ usr/iscsistart.c     |  3 +++
+ 12 files changed, 41 insertions(+), 8 deletions(-)
+
+diff --git a/etc/iscsid.conf b/etc/iscsid.conf
+index f7ecb6b3c2ab..cb77a777062d 100644
+--- a/etc/iscsid.conf
++++ b/etc/iscsid.conf
+@@ -305,3 +305,11 @@ node.session.iscsi.FastAbort = Yes
+ # a task management function like an ABORT TASK or LOGICAL UNIT RESET, that
+ # it continue to respond to R2Ts. To enable this uncomment this line
+ # node.session.iscsi.FastAbort = No
++
++# To prevent doing automatic scans that would add unwanted luns to the system
++# we can disable them and have sessions only do manually requested scans.
++# Automatic scans are performed on startup, on login, and on AEN/AER reception
++# on devices supporting it.  For HW drivers all sessions will use the value
++# defined in the configuration file.  This configuration option is independent
++# of scsi_mod scan parameter. (The default behavior is auto):
++node.session.scan = auto
+diff --git a/usr/config.h b/usr/config.h
+index 5b1bb1d624c5..3bcb93fe7322 100644
+--- a/usr/config.h
++++ b/usr/config.h
+@@ -190,6 +190,7 @@ typedef struct session_rec {
+ 	int					queue_depth;
+ 	int					initial_login_retry_max;
+ 	int					nr_sessions;
++	int					scan;
+ 	struct iscsi_auth_config		auth;
+ 	struct iscsi_session_timeout_config	timeo;
+ 	struct iscsi_error_timeout_config	err_timeo;
+diff --git a/usr/idbm.c b/usr/idbm.c
+index 3b8a5a20bec8..ff8d67f64c51 100644
+--- a/usr/idbm.c
++++ b/usr/idbm.c
+@@ -462,6 +462,9 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri)
+ 		      session.iscsi.MaxOutstandingR2T, IDBM_SHOW, num, 1);
+ 	__recinfo_int(SESSION_ERL, ri, r,
+ 		      session.iscsi.ERL, IDBM_SHOW, num, 1);
++	__recinfo_int_o2(SESSION_SCAN, ri, r,
++			 session.scan, IDBM_SHOW, "manual", "auto",
++			 num, 1);
+ 
+ 	for (i = 0; i < ISCSI_CONN_MAX; i++) {
+ 		char key[NAME_MAXVAL];
+@@ -2490,7 +2493,7 @@ static void idbm_rm_disc_node_links(char *disc_dir)
+ 		log_debug(5, "disc removal removing link %s %s %s %s",
+ 			  target, address, port, iface_id);
+ 
+-		memset(rec, 0, sizeof(*rec));	
++		memset(rec, 0, sizeof(*rec));
+ 		strlcpy(rec->name, target, TARGET_NAME_MAXLEN);
+ 		rec->tpgt = atoi(tpgt);
+ 		rec->conn[0].port = atoi(port);
+@@ -2726,6 +2729,14 @@ idbm_slp_defaults(struct iscsi_slp_config *cfg)
+ 	       sizeof(struct iscsi_slp_config));
+ }
+ 
++int
++idbm_session_autoscan(struct iscsi_session *session)
++{
++	if (session)
++		return session->nrec.session.scan;
++	return db->nrec.session.scan;
++}
++
+ struct user_param *idbm_alloc_user_param(char *name, char *value)
+ {
+ 	struct user_param *param;
+@@ -2981,6 +2992,7 @@ void idbm_node_setup_defaults(node_rec_t *rec)
+ 	rec->session.info = NULL;
+ 	rec->session.sid = 0;
+ 	rec->session.multiple = 0;
++	rec->session.scan = DEF_INITIAL_SCAN;
+ 	idbm_setup_session_defaults(&rec->session.iscsi);
+ 
+ 	for (i=0; i<ISCSI_CONN_MAX; i++) {
+diff --git a/usr/idbm.h b/usr/idbm.h
+index b9020fe4fd0a..411dd8230ece 100644
+--- a/usr/idbm.h
++++ b/usr/idbm.h
+@@ -140,6 +140,7 @@ extern int idbm_add_discovery(discovery_rec_t *newrec);
+ extern void idbm_sendtargets_defaults(struct iscsi_sendtargets_config *cfg);
+ extern void idbm_isns_defaults(struct iscsi_isns_config *cfg);
+ extern void idbm_slp_defaults(struct iscsi_slp_config *cfg);
++extern int idbm_session_autoscan(struct iscsi_session *session);
+ extern int idbm_discovery_read(discovery_rec_t *rec, int type, char *addr,
+ 				int port);
+ extern int idbm_rec_read(node_rec_t *out_rec, char *target_name,
+diff --git a/usr/idbm_fields.h b/usr/idbm_fields.h
+index 5790a033d241..4a927584c2fc 100644
+--- a/usr/idbm_fields.h
++++ b/usr/idbm_fields.h
+@@ -45,6 +45,7 @@
+ #define SESSION_MAX_CONNS	"node.session.iscsi.MaxConnections"
+ #define SESSION_MAX_R2T		"node.session.iscsi.MaxOutstandingR2T"
+ #define SESSION_ERL		"node.session.iscsi.ERL"
++#define SESSION_SCAN		"node.session.scan"
+ 
+ /* connections fields */
+ #define CONN_ADDR		"node.conn[%d].address"
+diff --git a/usr/initiator.c b/usr/initiator.c
+index ed174b5af38f..a86d1e6dee90 100644
+--- a/usr/initiator.c
++++ b/usr/initiator.c
+@@ -997,7 +997,7 @@ static void session_scan_host(struct iscsi_session *session, int hostno,
+ {
+ 	pid_t pid;
+ 
+-	pid = iscsi_sysfs_scan_host(hostno, 1);
++	pid = iscsi_sysfs_scan_host(hostno, 1, idbm_session_autoscan(session));
+ 	if (pid == 0) {
+ 		mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
+ 
+@@ -1201,7 +1201,8 @@ static void iscsi_recv_async_msg(iscsi_conn_t *conn, struct iscsi_hdr *hdr)
+ 			break;
+ 		}
+ 
+-		if (sshdr.asc == 0x3f && sshdr.ascq == 0x0e)
++		if (sshdr.asc == 0x3f && sshdr.ascq == 0x0e
++		    && idbm_session_autoscan(session))
+ 			session_scan_host(session, session->hostno, NULL);
+ 		break;
+ 	case ISCSI_ASYNC_MSG_REQUEST_LOGOUT:
+diff --git a/usr/iscsi_settings.h b/usr/iscsi_settings.h
+index 3d923c8ba029..296ff403b1e5 100644
+--- a/usr/iscsi_settings.h
++++ b/usr/iscsi_settings.h
+@@ -45,3 +45,6 @@
+ 
+ /* login retries */
+ #define DEF_INITIAL_LOGIN_RETRIES_MAX	4
++
++/* autoscan enabled */
++#define DEF_INITIAL_SCAN	1
+diff --git a/usr/iscsi_sysfs.c b/usr/iscsi_sysfs.c
+index 8ca668fdb3bc..2f94b632baaa 100644
+--- a/usr/iscsi_sysfs.c
++++ b/usr/iscsi_sysfs.c
+@@ -1883,12 +1883,15 @@ void iscsi_sysfs_rescan_device(void *data, int hostno, int target, int lun)
+ 			strlen(write_buf));
+ }
+ 
+-pid_t iscsi_sysfs_scan_host(int hostno, int async)
++pid_t iscsi_sysfs_scan_host(int hostno, int async, int full_scan)
+ {
+ 	char id[NAME_SIZE];
+-	char *write_buf = "- - -";
++	char write_buf[6] = "- - 0";
+ 	pid_t pid = 0;
+ 
++	if (full_scan)
++		write_buf[4] = '-';
++
+ 	if (async)
+ 		pid = fork();
+ 	if (pid == 0) {
+diff --git a/usr/iscsi_sysfs.h b/usr/iscsi_sysfs.h
+index 9a56105165b3..3492ce6e033c 100644
+--- a/usr/iscsi_sysfs.h
++++ b/usr/iscsi_sysfs.h
+@@ -87,7 +87,7 @@ extern void iscsi_sysfs_get_negotiated_session_conf(int sid,
+ 				struct iscsi_session_operational_config *conf);
+ extern void iscsi_sysfs_get_negotiated_conn_conf(int sid,
+ 				struct iscsi_conn_operational_config *conf);
+-extern pid_t iscsi_sysfs_scan_host(int hostno, int async);
++extern pid_t iscsi_sysfs_scan_host(int hostno, int async, int full);
+ extern int iscsi_sysfs_get_session_state(char *state, int sid);
+ extern int iscsi_sysfs_get_host_state(char *state, int host_no);
+ extern int iscsi_sysfs_get_device_state(char *state, int host_no, int target,
+diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
+index 4b2bd34cbb2e..90e2828bcb79 100644
+--- a/usr/iscsiadm.c
++++ b/usr/iscsiadm.c
+@@ -773,7 +773,7 @@ static int rescan_portal(void *data, struct session_info *info)
+ 	iscsi_sysfs_for_each_device(NULL, host_no, info->sid,
+ 				    iscsi_sysfs_rescan_device);
+ 	/* now scan for new devices */
+-	iscsi_sysfs_scan_host(host_no, 0);
++	iscsi_sysfs_scan_host(host_no, 0, 1);
+ 	return 0;
+ }
+ 
+diff --git a/usr/iscsid.c b/usr/iscsid.c
+index 81a14f259b5f..813eb3da940d 100644
+--- a/usr/iscsid.c
++++ b/usr/iscsid.c
+@@ -216,7 +216,7 @@ static int sync_session(void *data, struct session_info *info)
+ 				  iscsi_err_to_str(err));
+ 			return 0;
+ 		}
+-		iscsi_sysfs_scan_host(host_no, 0);
++		iscsi_sysfs_scan_host(host_no, 0, idbm_session_autoscan(NULL));
+ 		return 0;
+ 	}
+ 
+diff --git a/usr/iscsistart.c b/usr/iscsistart.c
+index 5cf09721636b..67ac47515f23 100644
+--- a/usr/iscsistart.c
++++ b/usr/iscsistart.c
+@@ -140,6 +140,7 @@ static int apply_params(struct node_rec *rec)
+ 	rec->session.initial_login_retry_max = -1;
+ 	rec->conn[0].timeo.noop_out_interval = -1;
+ 	rec->conn[0].timeo.noop_out_timeout = -1;
++	rec->session.scan = -1;
+ 
+ 	list_for_each_entry(param, &user_params, list) {
+ 		/*
+@@ -183,6 +184,8 @@ static int apply_params(struct node_rec *rec)
+ 		rec->conn[0].timeo.noop_out_interval = 0;
+ 	if (rec->conn[0].timeo.noop_out_timeout == -1)
+ 		rec->conn[0].timeo.noop_out_timeout = 0;
++	if (rec->session.scan == -1)
++		rec->session.scan = DEF_INITIAL_SCAN;
+ 
+ 	return 0;
+ }
+-- 
+2.9.3
+
diff --git a/SOURCES/open-iscsi-2.0.874-7-iscsid-Changes-to-support-the-new-qedi-transport.patch b/SOURCES/open-iscsi-2.0.874-7-iscsid-Changes-to-support-the-new-qedi-transport.patch
new file mode 100644
index 0000000..11e4272
--- /dev/null
+++ b/SOURCES/open-iscsi-2.0.874-7-iscsid-Changes-to-support-the-new-qedi-transport.patch
@@ -0,0 +1,73 @@
+From dc939e78d6dab78f3fcddfada581fa402604bb51 Mon Sep 17 00:00:00 2001
+From: Nilesh Javali <nilesh.javali@cavium.com>
+Date: Fri, 11 Nov 2016 08:17:50 +0200
+Subject: iscsid: Changes to support the new qedi transport
+
+Signed-off-by: Manish Rangankar <manish.rangankar@cavium.com>
+Signed-off-by: Adheer Chandravanshi <adheer.chandravanshi@qlogic.com>
+Signed-off-by: Nilesh Javali <nilesh.javali@cavium.com>
+---
+ usr/initiator_common.c |  2 +-
+ usr/transport.c        | 13 +++++++++++++
+ usr/transport.h        |  1 +
+ 3 files changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/usr/initiator_common.c b/usr/initiator_common.c
+index 1d1d82251ad4..191e779bb942 100644
+--- a/usr/initiator_common.c
++++ b/usr/initiator_common.c
+@@ -700,7 +700,7 @@ int iscsi_host_set_net_params(struct iface_rec *iface,
+ 		netdev = hinfo.iface.netdev;
+ 	}
+ 
+-	if (net_ifup_netdev(netdev))
++	if (!t->template->no_netdev && net_ifup_netdev(netdev))
+ 		log_warning("Could not brining up netdev %s. Try running "
+ 			    "'ifup %s' first if login fails.", netdev, netdev);
+ 
+diff --git a/usr/transport.c b/usr/transport.c
+index 18b770464608..533ba30a8846 100644
+--- a/usr/transport.c
++++ b/usr/transport.c
+@@ -114,6 +114,18 @@ struct iscsi_transport_template ocs = {
+ 	.ep_disconnect	= ktransport_ep_disconnect,
+ };
+ 
++struct iscsi_transport_template qedi = {
++	.name		= "qedi",
++	.set_host_ip	= SET_HOST_IP_REQ,
++	.use_boot_info	= 1,
++	.bind_ep_required = 1,
++	.no_netdev = 1,
++	.ep_connect	= ktransport_ep_connect,
++	.ep_poll	= ktransport_ep_poll,
++	.ep_disconnect	= ktransport_ep_disconnect,
++	.set_net_config = uip_broadcast_params,
++};
++
+ static struct iscsi_transport_template *iscsi_transport_templates[] = {
+ 	&iscsi_tcp,
+ 	&iscsi_iser,
+@@ -123,6 +135,7 @@ static struct iscsi_transport_template *iscsi_transport_templates[] = {
+ 	&qla4xxx,
+ 	&be2iscsi,
+ 	&ocs,
++	&qedi,
+ 	NULL
+ };
+ 
+diff --git a/usr/transport.h b/usr/transport.h
+index 4d3bdbff67f8..b67776b47288 100644
+--- a/usr/transport.h
++++ b/usr/transport.h
+@@ -39,6 +39,7 @@ struct iscsi_transport_template {
+ 	uint8_t set_host_ip;
+ 	uint8_t use_boot_info;
+         uint8_t bind_ep_required;
++	uint8_t no_netdev;
+ 	int (*ep_connect) (struct iscsi_conn *conn, int non_blocking);
+ 	int (*ep_poll) (struct iscsi_conn *conn, int timeout_ms);
+ 	void (*ep_disconnect) (struct iscsi_conn *conn);
+-- 
+2.9.3
+
diff --git a/SOURCES/open-iscsi-2.0.874-8-iscsiuio-Add-support-for-the-new-qedi-transport.patch b/SOURCES/open-iscsi-2.0.874-8-iscsiuio-Add-support-for-the-new-qedi-transport.patch
new file mode 100644
index 0000000..83234c9
--- /dev/null
+++ b/SOURCES/open-iscsi-2.0.874-8-iscsiuio-Add-support-for-the-new-qedi-transport.patch
@@ -0,0 +1,1665 @@
+From 264e48a0bda2d6cd5d0607acd2669894ee95b3b5 Mon Sep 17 00:00:00 2001
+From: Nilesh Javali <nilesh.javali@cavium.com>
+Date: Fri, 11 Nov 2016 08:17:51 +0200
+Subject: iscsiuio: Add support for the new qedi transport
+
+Signed-off-by: Manish Rangankar <manish.rangankar@cavium.com>
+Signed-off-by: Adheer Chandravanshi <adheer.chandravanshi@qlogic.com>
+Signed-off-by: Nilesh Javali <nilesh.javali@cavium.com>
+---
+ iscsiuio/src/unix/libs/Makefile.am |    3 +-
+ iscsiuio/src/unix/libs/cnic.c      |    9 +
+ iscsiuio/src/unix/libs/cnic.h      |    2 +
+ iscsiuio/src/unix/libs/qedi.c      | 1151 ++++++++++++++++++++++++++++++++++++
+ iscsiuio/src/unix/libs/qedi.h      |  159 +++++
+ iscsiuio/src/unix/nic.c            |    6 +-
+ iscsiuio/src/unix/nic.h            |    1 +
+ iscsiuio/src/unix/nic_utils.c      |  147 ++++-
+ iscsiuio/src/unix/nic_utils.h      |    2 +
+ iscsiuio/src/unix/options.h        |    1 +
+ 10 files changed, 1475 insertions(+), 6 deletions(-)
+ create mode 100644 iscsiuio/src/unix/libs/qedi.c
+ create mode 100644 iscsiuio/src/unix/libs/qedi.h
+
+diff --git a/iscsiuio/src/unix/libs/Makefile.am b/iscsiuio/src/unix/libs/Makefile.am
+index 890415f5a79a..737546b04917 100644
+--- a/iscsiuio/src/unix/libs/Makefile.am
++++ b/iscsiuio/src/unix/libs/Makefile.am
+@@ -10,4 +10,5 @@ noinst_LIBRARIES = lib_iscsiuio_hw_cnic.a
+ lib_iscsiuio_hw_cnic_a_SOURCES =	../build_date.c \
+ 					cnic.c 		\
+ 					bnx2.c		\
+-					bnx2x.c
++					bnx2x.c		\
++					qedi.c
+diff --git a/iscsiuio/src/unix/libs/cnic.c b/iscsiuio/src/unix/libs/cnic.c
+index 228c4b9e25b1..5d60f898ad57 100644
+--- a/iscsiuio/src/unix/libs/cnic.c
++++ b/iscsiuio/src/unix/libs/cnic.c
+@@ -55,6 +55,7 @@
+ #include <sys/types.h>
+ #include <sys/user.h>
+ #include <sys/socket.h>
++#include <sys/mman.h>
+ 
+ #include "uip_arp.h"
+ #include "nic.h"
+@@ -65,6 +66,7 @@
+ #include "cnic.h"
+ #include "iscsi_if.h"
+ #include "ipv6_ndpc.h"
++#include "qedi.h"
+ 
+ /*******************************************************************************
+  * Constants
+@@ -81,6 +83,13 @@ const char bnx2i_library_transport_name[] = "bnx2i";
+ const size_t bnx2i_library_transport_name_size =
+ 			sizeof(bnx2i_library_transport_name);
+ 
++/*******************************************************************************
++ * Constants for qedi module
++ ******************************************************************************/
++const char qedi_library_transport_name[] = "qedi";
++const size_t qedi_library_transport_name_size =
++			sizeof(qedi_library_transport_name);
++
+ /******************************************************************************
+  * Netlink Functions
+  ******************************************************************************/
+diff --git a/iscsiuio/src/unix/libs/cnic.h b/iscsiuio/src/unix/libs/cnic.h
+index 6244a94012c1..c86595c512b0 100644
+--- a/iscsiuio/src/unix/libs/cnic.h
++++ b/iscsiuio/src/unix/libs/cnic.h
+@@ -44,6 +44,8 @@
+  ******************************************************************************/
+ extern const char bnx2i_library_transport_name[];
+ extern const size_t bnx2i_library_transport_name_size;
++extern const char qedi_library_transport_name[];
++extern const size_t qedi_library_transport_name_size;
+ 
+ int cnic_nl_open();
+ void cnic_nl_close();
+diff --git a/iscsiuio/src/unix/libs/qedi.c b/iscsiuio/src/unix/libs/qedi.c
+new file mode 100644
+index 000000000000..c2096e59dad1
+--- /dev/null
++++ b/iscsiuio/src/unix/libs/qedi.c
+@@ -0,0 +1,1151 @@
++/*
++ * Copyright (c) 2016, Cavium Inc.
++ *
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ *    must display the following acknowledgement:
++ *      This product includes software developed by Adam Dunkels.
++ * 4. The name of the author may not be used to endorse or promote
++ *    products derived from this software without specific prior
++ *    written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
++ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
++ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
++ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ * qedi.c - qedi user space driver
++ * This file handles different qedi NIC operations,
++ * qedi_open - initializes all hardware resources under NIC device
++ * qedi_close - closes the NIC device
++ * qedi_read - reads data to the hardware
++ * qedi_write - writes data to the hardware
++ * qedi_start_xmit - sends a pkt of data on NIC device
++ * qedi_get_tx_pkt - gets a Tx pkt from NIC
++ * qedi_clear_tx_intr - clears the Tx interrupt
++ * NOTE: nic_t is used as NIC device,
++ * 	 qedi is not attached to netdev hence it is not mandatory
++ * 	 for netdev to be upd
++ */
++#include <errno.h>
++#include <stdio.h>
++#include <string.h>
++#include <arpa/inet.h>
++#include <linux/types.h>
++#include <linux/sockios.h>
++#include <linux/ethtool.h>
++#include <linux/netlink.h>
++#include <sys/mman.h>
++#include <sys/ioctl.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/user.h>
++#include <fcntl.h>
++#include <unistd.h>
++
++#include "config.h"
++
++#include "build_date.h"
++#include "bnx2x.h"
++#include "qedi.h"
++#include "cnic.h"
++#include "logger.h"
++#include "nic.h"
++#include "nic_id.h"
++#include "nic_utils.h"
++#include "options.h"
++
++#define PFX	"qedi "
++
++extern int nl_sock;
++
++/*  Foward struct declarations */
++struct nic_ops qedi_op;
++
++/*******************************************************************************
++ * NIC Library Strings
++ ******************************************************************************/
++static const char library_name[] = "qedi";
++static const char library_version[] = PACKAGE_VERSION;
++static const char library_uio_name[] = "qedi_uio";
++
++/*  The name that should be returned from /sys/class/uio/uio0/name */
++static const char cnic_uio_sysfs_name_tempate[] = "/sys/class/uio/uio%i/name";
++static const char qedi_uio_sysfs_name[] = "qedi_uio";
++static const char qedi_host_mac_template[] =
++	"/sys/class/iscsi_host/host%i/hwaddress";
++
++struct qedi_driver_version qedi_version = {
++	QEDI_UNKNOWN_MAJOR_VERSION,
++	QEDI_UNKNOWN_MINOR_VERSION,
++	QEDI_UNKNOWN_SUB_MINOR_VERSION,
++};
++
++static int qedi_clear_tx_intr(nic_t *nic);
++
++/*******************************************************************************
++ * QEDI Library Functions
++ ******************************************************************************/
++/**
++ *  qedi_get_library_name() - Used to get the name of this NIC library
++ *  @param name - This function will return the pointer to this NIC
++ *                library name
++ *  @param name_size
++ */
++static void qedi_get_library_name(char **name, size_t *name_size)
++{
++	*name = (char *)library_name;
++	*name_size = sizeof(library_name);
++}
++
++/**
++ *  qedi_get_library_version() - Used to get the version string of this
++ *                                NIC library
++ *  @param version - This function will return the pointer to this NIC
++ *                   library version string
++ *  @param version_size - This will be set with the version size
++ */
++static void qedi_get_library_version(char **version, size_t *version_size)
++{
++	*version = (char *)library_version;
++	*version_size = sizeof(library_version);
++}
++
++/**
++ *  qedi_get_build_date() - Used to get the build date string of this library
++ *  @param version - This function will return the pointer to this NIC
++ *                   library build date string
++ *  @param version_size - This will be set with the build date string size
++ */
++static void qedi_get_build_date(char **build, size_t *build_size)
++{
++	*build = (char *)build_date;
++	*build_size = sizeof(build_date);
++}
++
++/**
++ *  qedi_get_transport_name() - Used to get the transport name associated
++ *                              with this this NIC library
++ *  @param transport_name - This function will return the pointer to this NIC
++ *                          library's associated transport string
++ *  @param transport_name_size - This will be set with the transport name size
++ */
++static void qedi_get_transport_name(char **transport_name,
++				    size_t *transport_name_size)
++{
++	*transport_name = (char *)qedi_library_transport_name;
++	*transport_name_size = qedi_library_transport_name_size;
++}
++
++/**
++ *  qedi_get_uio_name() - Used to get the uio name associated with this this
++ *                        NIC library
++ *  @param uio_name - This function will return the pointer to this NIC
++ *                    library's associated uio string
++ *  @param transport_name_size - This will be set with the uio name size
++ */
++static void qedi_get_uio_name(char **uio_name, size_t *uio_name_size)
++{
++	*uio_name = (char *)library_uio_name;
++	*uio_name_size = sizeof(library_uio_name);
++}
++
++/**
++ *  qedi_get_ops() - Used to get the NIC library op table
++ *  @param op - The op table of this NIC library
++ */
++struct nic_ops *qedi_get_ops()
++{
++	return &qedi_op;
++}
++
++/*******************************************************************************
++ * qedi Utility Functions
++ ******************************************************************************/
++/*******************************************************************************
++ * Utility Functions Used to read register from the qedi device
++ ******************************************************************************/
++static void qedi_set_drv_version_unknown(qedi_t *bp)
++{
++	bp->version.major = QEDI_UNKNOWN_MAJOR_VERSION;
++	bp->version.minor = QEDI_UNKNOWN_MINOR_VERSION;
++	bp->version.sub_minor = QEDI_UNKNOWN_SUB_MINOR_VERSION;
++}
++
++/* Return: 1 = Unknown, 0 = Known */
++static int qedi_is_drv_version_unknown(struct qedi_driver_version *version)
++{
++	if ((version->major == (uint16_t)QEDI_UNKNOWN_MAJOR_VERSION) &&
++	    (version->minor == (uint16_t)QEDI_UNKNOWN_MINOR_VERSION) &&
++	    (version->sub_minor == (uint16_t)QEDI_UNKNOWN_SUB_MINOR_VERSION)) {
++		return 1;
++	}
++
++	return 0;
++}
++
++/**
++ * qedi_get_drv_version() - Used to determine the driver version
++ * @param bp - Device used to determine qedi driver version
++ */
++static int qedi_get_drv_version(qedi_t *bp)
++{
++	nic_t *nic = bp->parent;
++
++	/*
++	 * CAPABILITIES: Get the iscsi driver version from qedi
++	 * This may be obtained from sysfs
++	 */
++	LOG_INFO(PFX "%s: qedi driver using version %d.%d.%d",
++		 nic->log_name,
++		 bp->version.major, bp->version.minor, bp->version.sub_minor);
++
++	return 0;
++}
++
++/******************************************************************************/
++
++/**
++ * qedi_get_chip_id() - Used to retrieve the chip ID from the nic
++ * @param dev - Device used to determin NIC type
++ * @return Chip ID read from the MISC ID register
++ */
++static int qedi_get_chip_id(qedi_t *bp)
++{
++	/* int val, id; */
++
++	/* Get the chip revision id and number. */
++	/* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */
++	/*
++	 * CAPABILITIES: Get the CHIP info from qedi through sysfs or uio struct.
++	 */
++	return 0;
++}
++
++/**
++ *  qedi_uio_verify()
++ *
++ */
++static int qedi_uio_verify(nic_t *nic)
++{
++	char *raw = NULL, *raw_tmp;
++	uint32_t raw_size = 0;
++	char temp_path[sizeof(cnic_uio_sysfs_name_tempate) + 8];
++	int rc = 0;
++
++	/*  Build the path to determine uio name */
++	snprintf(temp_path, sizeof(temp_path),
++		 cnic_uio_sysfs_name_tempate, nic->uio_minor);
++
++	rc = capture_file(&raw, &raw_size, temp_path);
++	if (rc != 0)
++		goto error;
++
++	/* sanitize name string by replacing newline with null termination */
++	raw_tmp = raw;
++	while (*raw_tmp != '\n')
++		raw_tmp++;
++	*raw_tmp = '\0';
++
++	if (strncmp(raw, qedi_uio_sysfs_name,
++		    sizeof(qedi_uio_sysfs_name)) != 0) {
++		LOG_ERR(PFX "%s: uio names not equal: expecting %s got %s from %s",
++			nic->log_name, qedi_uio_sysfs_name, raw, temp_path);
++		rc = -EIO;
++	}
++
++	free(raw);
++
++	LOG_INFO(PFX "%s: Verified is a qedi_uio device", nic->log_name);
++
++error:
++	return rc;
++}
++
++static int qedi_get_mac_addr(qedi_t *bp)
++{
++	nic_t *nic = bp->parent;
++	char *raw = NULL, *raw_tmp;
++	uint32_t raw_size = 0;
++	char temp_path[sizeof(qedi_host_mac_template) + 8];
++	int rc = 0;
++
++	/*  Build the path to determine mac address */
++	snprintf(temp_path, sizeof(temp_path),
++		 qedi_host_mac_template, nic->host_no);
++
++	rc = capture_file(&raw, &raw_size, temp_path);
++	if (rc != 0)
++		goto error;
++
++	/* sanitize name string by replacing newline with null termination */
++	raw_tmp = raw;
++	while (*raw_tmp != '\n')
++		raw_tmp++;
++	*raw_tmp = '\0';
++
++	rc = sscanf(raw, "%02x:%02x:%02x:%02x:%02x:%02x",
++	       (uint32_t *)&nic->mac_addr[0], (uint32_t *)&nic->mac_addr[1],
++	       (uint32_t *)&nic->mac_addr[2], (uint32_t *)&nic->mac_addr[3],
++	       (uint32_t *)&nic->mac_addr[4], (uint32_t *)&nic->mac_addr[5]);
++	if (rc != 1) {
++		LOG_WARN(PFX "%s: Could not parse mac_addr",
++			nic->log_name);
++		rc = -ENODEV;
++		goto error;
++	}
++
++error:
++	if (raw)
++		free(raw);
++	return rc;
++}
++
++/*******************************************************************************
++ * qedi Utility Functions to get to the hardware consumer indexes
++ ******************************************************************************/
++
++static __u32 qedi_get_rx(qedi_t *bp)
++{
++	return ((struct qedi_uio_ctrl *)bp->uctrl_map)->host_rx_cons;
++}
++
++static __u32 qedi_get_tx(qedi_t *bp)
++{
++	return ((struct qedi_uio_ctrl *)bp->uctrl_map)->hw_tx_cons;
++}
++
++/**
++ *  qedi_free() - Used to free a qedi structure
++ */
++static void qedi_free(nic_t *nic)
++{
++	if (nic->priv)
++		free(nic->priv);
++	nic->priv = NULL;
++}
++
++/**
++ *  qedi_alloc() - Used to allocate a qedi structure
++ */
++static qedi_t *qedi_alloc(nic_t *nic)
++{
++	qedi_t *bp = malloc(sizeof(*bp));
++
++	if (!bp) {
++		LOG_ERR(PFX "%s: Could not allocate QEDI space",
++			nic->log_name);
++		return NULL;
++	}
++
++	/*  Clear out the CNIC contents */
++	memset(bp, 0, sizeof(*bp));
++
++	bp->parent = nic;
++	nic->priv = (void *)bp;
++	get_iscsi_transport_handle(nic, &nic->transport_handle);
++	qedi_set_drv_version_unknown(bp);
++
++	return bp;
++}
++
++int uio_get_map_offset(nic_t *nic, uint8_t map, uint32_t *offset)
++{
++	char *raw = NULL;
++	uint32_t raw_size = 0;
++	ssize_t elements_read;
++	char temp_path[sizeof(UIO_OFFSET_TMPL) + 8];
++	int rc = 0;
++
++	/*  Capture RX buffer size */
++	snprintf(temp_path, sizeof(temp_path),
++		 UIO_OFFSET_TMPL, nic->uio_minor, map);
++
++	rc = capture_file(&raw, &raw_size, temp_path);
++	if (rc != 0)
++		goto error;
++
++	elements_read = sscanf(raw, "0x%x", offset);
++	if (elements_read != 1) {
++		LOG_ERR(PFX "%s: Couldn't get the offset from %s",
++			nic->log_name, temp_path);
++		rc = -EIO;
++		goto error;
++	}
++
++	rc = 0;
++error:
++	if (raw)
++		free(raw);
++
++	return rc;
++}
++
++int uio_get_map_info(nic_t *nic, uint8_t map, char *attr, uint32_t *val)
++{
++	char *raw = NULL;
++	uint32_t raw_size = 0;
++	ssize_t elements_read;
++	char temp_path[sizeof(UIO_ATTR_TMPL) + 8];
++	int rc = 0;
++
++	/*  Capture RX buffer size */
++	snprintf(temp_path, sizeof(temp_path),
++		 UIO_ATTR_TMPL, nic->uio_minor, map, attr);
++
++	rc = capture_file(&raw, &raw_size, temp_path);
++	if (rc != 0)
++		goto error;
++
++	elements_read = sscanf(raw, "0x%x", val);
++	if (elements_read != 1) {
++		LOG_ERR(PFX "%s: Couldn't get the offset from %s",
++			nic->log_name, temp_path);
++		rc = -EIO;
++		goto error;
++	}
++
++	rc = 0;
++error:
++	if (raw)
++		free(raw);
++
++	return rc;
++}
++
++/**
++ * qedi_open() - This will initialize all the hardware resources underneath
++ *               a struct cnic_uio device
++ * @param dev - The struct cnic_uio device to attach the hardware with
++ * @return 0 on success, on failure a errno will be returned
++ */
++static int qedi_open(nic_t *nic)
++{
++	qedi_t *bp = NULL;
++	struct stat uio_stat;
++	int i, rc;
++	int count;
++	uint32_t bus;
++	uint32_t slot;
++	uint32_t func;
++	uint32_t offset;
++
++	/*  Sanity Check: validate the parameters */
++	if (!nic) {
++		LOG_ERR(PFX "nic == NULL");
++		return -EINVAL;
++	}
++
++	if ((nic->priv) != NULL &&
++	    (((qedi_t *)(nic->priv))->flags & QEDI_OPENED)) {
++		return 0;
++	}
++
++	if (nic->host_no == INVALID_HOST_NO) {
++		rc = sscanf(nic->config_device_name, "host%d", &nic->host_no);
++		if (rc != 1) {
++			LOG_WARN(PFX "%s: Could not parse for host number",
++				 nic->config_device_name);
++			rc = -ENODEV;
++			goto open_error;
++		}
++	}
++
++	bp = qedi_alloc(nic);
++	if (!bp)
++		return -ENOMEM;
++
++	if (qedi_is_drv_version_unknown(&qedi_version)) {
++		/* If version is unknown, go read from ethtool */
++		rc = qedi_get_drv_version(bp);
++		if (rc)
++			goto open_error;
++	} else {
++		/* Version is not unknown, just use it */
++		qedi_version.major = bp->version.major;
++		qedi_version.minor = bp->version.minor;
++		qedi_version.sub_minor = bp->version.sub_minor;
++	}
++
++	count = 0;
++	while ((nic->fd < 0) && count < 15) {
++		/*  udev might not have created the file yet */
++		pthread_mutex_unlock(&nic->nic_mutex);
++		sleep(1);
++		pthread_mutex_lock(&nic->nic_mutex);
++
++		nic->fd = open(nic->uio_device_name, O_RDWR | O_NONBLOCK);
++		if (nic->fd != INVALID_FD) {
++			LOG_ERR(PFX "%s: uio device has been brought up via pid: %d on fd: %d",
++				nic->uio_device_name, getpid(), nic->fd);
++
++			rc = qedi_uio_verify(nic);
++			if (rc != 0)
++				continue;
++
++			break;
++		} else {
++			LOG_WARN(PFX "%s: Could not open device: %s, [%s]",
++				 nic->log_name, nic->uio_device_name,
++				 strerror(errno));
++
++			manually_trigger_uio_event(nic, nic->uio_minor);
++
++			/*  udev might not have created the file yet */
++			pthread_mutex_unlock(&nic->nic_mutex);
++			sleep(1);
++			pthread_mutex_lock(&nic->nic_mutex);
++
++			count++;
++		}
++	}
++	if (fstat(nic->fd, &uio_stat) < 0) {
++		LOG_ERR(PFX "%s: Could not fstat device", nic->log_name);
++		rc = -ENODEV;
++		goto open_error;
++	}
++	nic->uio_minor = minor(uio_stat.st_rdev);
++
++	/*
++	 * CAPABILITIES: acquire the rx buffer size and rx ring size from qedi
++	 */
++
++	bp->rx_ring_size = RX_RING_SIZE;
++	bp->rx_buffer_size = PKT_BUF_SIZE;
++
++	LOG_DEBUG(PFX "%s: using rx ring size: %d, rx buffer size: %d",
++		  nic->log_name, bp->rx_ring_size, bp->rx_buffer_size);
++
++	/* Determine the number of UIO events that have already occurred */
++	rc = detemine_initial_uio_events(nic, &nic->intr_count);
++	if (rc != 0) {
++		LOG_ERR(PFX "Could not get the no. of initial UIO events");
++		nic->intr_count = 0;
++	}
++
++	/* Allocate space for rx pkt ring */
++	bp->rx_pkt_ring = malloc(sizeof(void *) * bp->rx_ring_size);
++	if (!bp->rx_pkt_ring) {
++		LOG_ERR(PFX "%s: Could not allocate space for rx_pkt_ring",
++			nic->log_name);
++		rc = errno;
++		goto open_error;
++	}
++
++	/*
++	 * Map the uio struct and packet buffer
++	 */
++	offset = 0;
++	rc = uio_get_map_info(nic, QEDI_UCTRL_MAP_REG, "size", &offset);
++	if (rc) {
++		LOG_INFO(PFX "Failed to get the map size rc=%d", rc);
++		goto open_error;
++	}
++	LOG_INFO(PFX "uctrl map size=%u", offset);
++
++	offset = 0;
++	rc = uio_get_map_info(nic, QEDI_RING_MAP_REG, "size", &offset);
++	if (rc) {
++		LOG_INFO(PFX "Failed to get the map size rc=%d", rc);
++		goto open_error;
++	}
++	LOG_INFO(PFX "ring map size=%u", offset);
++
++	offset = 0;
++	rc = uio_get_map_info(nic, QEDI_BUF_MAP_REG, "size", &offset);
++	if (rc) {
++		LOG_INFO(PFX "Failed to get the map size rc=%d", rc);
++		goto open_error;
++	}
++	LOG_INFO(PFX "buf map size=%u", offset);
++
++	offset = 0;
++	rc = uio_get_map_offset(nic, QEDI_UCTRL_MAP_REG, &offset);
++	if (rc) {
++		LOG_INFO(PFX "Failed to get the map offset rc=%d", rc);
++		goto open_error;
++	}
++
++	bp->uctrl_map = mmap(NULL, sizeof(struct qedi_uio_ctrl),
++			    PROT_READ | PROT_WRITE,
++			    MAP_SHARED | MAP_LOCKED,
++			    nic->fd, (off_t)0);
++	if (bp->uctrl_map == MAP_FAILED) {
++		LOG_INFO(PFX "%s: Could not mmap uio ctrl struct: %s",
++			 nic->log_name, strerror(errno));
++		bp->uctrl_map = NULL;
++		rc = errno;
++		goto open_error;
++	}
++
++	bp->uctrl_map_offset = offset;
++	bp->uctrl_map += offset;
++
++	bp->rx_comp_ring = mmap(NULL, nic->page_size,
++			   PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED,
++			   nic->fd, (off_t)nic->page_size);
++	if (bp->rx_comp_ring == MAP_FAILED) {
++		LOG_INFO(PFX "%s: Could not mmap rx_comp_ring: %s",
++			 nic->log_name, strerror(errno));
++		bp->rx_comp_ring = NULL;
++		rc = errno;
++		goto open_error;
++	}
++
++	bp->bufs = mmap(NULL, (bp->rx_ring_size + 1) * bp->rx_buffer_size,
++			PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED,
++			nic->fd, (off_t)2 * nic->page_size);
++	if (bp->bufs == MAP_FAILED) {
++		LOG_INFO(PFX "%s: Could not mmap pkt buffers: %s",
++			 nic->log_name, strerror(errno));
++		bp->bufs = NULL;
++		rc = errno;
++		goto open_error;
++	}
++
++	/*
++	 * Get all CHIP related info from qedi
++	 */
++	bp->chip_id = qedi_get_chip_id(bp);
++	LOG_DEBUG(PFX "Chip ID: %x", bp->chip_id);
++
++	rc = get_bus_slot_func_num(nic, &bus, &slot, &func);
++	if (rc != 0) {
++		LOG_INFO(PFX "%s: Couldn't determine bus:slot.func",
++			 nic->log_name);
++		goto open_error;
++	}
++
++	/*
++	 * Get all function, pfid, client_id and cid info from qedi
++	 */
++	LOG_INFO(PFX "%s: func 0x%x, pfid 0x%x, client_id 0x%x, cid 0x%x",
++		 nic->log_name, bp->func, bp->pfid, bp->client_id, bp->cid);
++
++	bp->get_rx_cons = qedi_get_rx;
++	bp->get_tx_cons = qedi_get_tx;
++	bp->tx_cons = 0;
++	bp->tx_prod = 0;
++	bp->tx_bd_prod = 0;
++	bp->tx_pkt = bp->bufs;
++	bp->rx_pkts = bp->bufs + bp->rx_buffer_size;
++
++	bp->rx_index = 0;
++	bp->rx_cons = 0;
++	bp->rx_bd_cons = 0;
++	bp->rx_prod = 127;
++	bp->rx_bd_prod = bp->rx_ring_size;
++
++	for (i = 0; i < bp->rx_ring_size; i++) {
++		void *ptr = bp->bufs + (bp->rx_buffer_size * (i + 1));
++
++		bp->rx_pkt_ring[i] = ptr;
++	}
++
++	qedi_get_mac_addr(bp);
++	LOG_INFO(PFX "%s:  Using mac address: %02x:%02x:%02x:%02x:%02x:%02x",
++		 nic->log_name,
++		 nic->mac_addr[0], nic->mac_addr[1], nic->mac_addr[2],
++		 nic->mac_addr[3], nic->mac_addr[4], nic->mac_addr[5]);
++
++	qedi_get_library_name(&nic->library_name, (size_t *)&count);
++	LOG_INFO("%s: qedi initialized", nic->log_name);
++
++	bp->flags |= QEDI_OPENED;
++
++	return 0;
++
++open_error:
++
++	if (bp->bufs) {
++		munmap(bp->bufs, (bp->rx_ring_size + 1) * bp->rx_buffer_size);
++		bp->bufs = NULL;
++	}
++
++	if (bp->rx_comp_ring) {
++		munmap(bp->rx_comp_ring, nic->page_size);
++		bp->rx_comp_ring = NULL;
++	}
++
++	if (bp->uctrl_map) {
++		bp->uctrl_map -= bp->uctrl_map_offset;
++		munmap(bp->uctrl_map, sizeof(struct qedi_uio_ctrl));
++		bp->uctrl_map = NULL;
++	}
++
++	if (bp->rx_pkt_ring) {
++		free(bp->rx_pkt_ring);
++		bp->rx_pkt_ring = NULL;
++	}
++
++	if (nic->fd != INVALID_FD) {
++		close(nic->fd);
++		nic->fd = INVALID_FD;
++	}
++
++	qedi_free(nic);
++
++	return rc;
++}
++
++/**
++ *  qedi_uio_close_resources() - Used to free resource for the NIC/CNIC
++ *  @param nic - NIC device to free resource
++ *  @param graceful - whether to wait to close gracefully
++ *  @return 0 on success, <0 on failure
++ */
++static int qedi_uio_close_resources(nic_t *nic, NIC_SHUTDOWN_T graceful)
++{
++	qedi_t *bp = (qedi_t *)nic->priv;
++	int rc = 0;
++
++	/*  Check if there is an assoicated qedi device */
++	if (!bp) {
++		LOG_WARN(PFX "%s: when closing resources there is no assoicated qedi",
++			 nic->log_name);
++		return -EIO;
++	}
++
++	/*  Clean up allocated memory */
++
++	if (bp->rx_pkt_ring) {
++		free(bp->rx_pkt_ring);
++		bp->rx_pkt_ring = NULL;
++	}
++
++	/*  Clean up mapped registers */
++	if (bp->bufs) {
++		rc = munmap(bp->bufs,
++			    (bp->rx_ring_size + 1) * bp->rx_buffer_size);
++		if (rc != 0)
++			LOG_ERR(PFX "%s: Couldn't unmap bufs", nic->log_name);
++		bp->bufs = NULL;
++	}
++
++	if (bp->rx_comp_ring) {
++		rc = munmap(bp->rx_comp_ring, nic->page_size);
++		if (rc != 0)
++			LOG_ERR(PFX "%s: Couldn't unmap ring", nic->log_name);
++		bp->rx_comp_ring = NULL;
++	}
++
++	if (bp->uctrl_map) {
++		bp->uctrl_map -= bp->uctrl_map_offset;
++		rc = munmap(bp->uctrl_map, sizeof(struct qedi_uio_ctrl));
++		if (rc != 0) {
++			LOG_ERR(PFX "%s: Couldn't unmap uio ctrl",
++				nic->log_name);
++		}
++		bp->uctrl_map = NULL;
++	}
++
++	if (nic->fd != INVALID_FD) {
++		rc = close(nic->fd);
++		if (rc != 0) {
++			LOG_ERR(PFX
++				 "%s: Couldn't close uio file descriptor: %d",
++				 nic->log_name, nic->fd);
++		} else {
++			LOG_DEBUG(PFX "%s: Closed uio file descriptor: %d",
++				  nic->log_name, nic->fd);
++		}
++
++		nic->fd = INVALID_FD;
++	} else {
++		LOG_ERR(PFX "%s: Invalid uio file descriptor: %d",
++			nic->log_name, nic->fd);
++	}
++
++	qedi_set_drv_version_unknown(bp);
++
++	LOG_INFO(PFX "%s: Closed all resources", nic->log_name);
++
++	return 0;
++}
++
++/**
++ *  qedi_close() - Used to close the NIC device
++ *  @param nic - NIC device to close
++ *  @param graceful - whether to wait to close gracefully
++ *  @return 0 if successful, <0 if there is an error
++ */
++static int qedi_close(nic_t *nic, NIC_SHUTDOWN_T graceful)
++{
++	/*  Sanity Check: validate the parameters */
++	if (!nic) {
++		LOG_ERR(PFX "%s: nic == NULL", __func__);
++		return -EINVAL;
++	}
++	if (!nic->priv) {
++		LOG_ERR(PFX "%s: nic->priv == NULL", __func__);
++		return -EINVAL;
++	}
++
++	LOG_INFO(PFX "Closing NIC device: %s", nic->log_name);
++
++	qedi_uio_close_resources(nic, graceful);
++	qedi_free(nic);
++
++	return 0;
++}
++
++static void qedi_prepare_xmit_packet(nic_t *nic,
++				     nic_interface_t *nic_iface,
++				     struct packet *pkt)
++{
++	qedi_t *bp = (qedi_t *)nic->priv;
++	struct uip_vlan_eth_hdr *eth_vlan = (struct uip_vlan_eth_hdr *)pkt->buf;
++	struct uip_eth_hdr *eth = (struct uip_eth_hdr *)bp->tx_pkt;
++
++	if (eth_vlan->tpid == htons(UIP_ETHTYPE_8021Q)) {
++		memcpy(bp->tx_pkt, pkt->buf, sizeof(struct uip_eth_hdr));
++		eth->type = eth_vlan->type;
++		pkt->buf_size -= (sizeof(struct uip_vlan_eth_hdr) -
++				  sizeof(struct uip_eth_hdr));
++		memcpy(bp->tx_pkt + sizeof(struct uip_eth_hdr),
++		       pkt->buf + sizeof(struct uip_vlan_eth_hdr),
++		       pkt->buf_size - sizeof(struct uip_eth_hdr));
++	} else {
++		memcpy(bp->tx_pkt, pkt->buf, pkt->buf_size);
++	}
++
++	msync(bp->tx_pkt, pkt->buf_size, MS_SYNC);
++}
++
++/**
++ *  qedi_get_tx_pkt() - This function is used to a TX packet from the NIC
++ *  @param nic - The NIC device to send the packet
++ */
++void *qedi_get_tx_pkt(nic_t *nic)
++{
++	qedi_t *bp = (qedi_t *)nic->priv;
++
++	return bp->tx_pkt;
++}
++
++/**
++ *  qedi_start_xmit() - This function is used to send a packet of data
++ *  @param nic - The NIC device to send the packet
++ *  @param len - the length of the TX packet
++ *
++ */
++void qedi_start_xmit(nic_t *nic, size_t len, u16_t vlan_id)
++{
++	qedi_t *bp = (qedi_t *)nic->priv;
++	uint8_t *ubuf;
++	struct iscsi_uevent *ev;
++	struct iscsi_path *path_data;
++	struct qedi_uio_ctrl *uctrl;
++	int rc = 0;
++	uint16_t buflen;
++
++	uctrl = (struct qedi_uio_ctrl *)bp->uctrl_map;
++
++	buflen = sizeof(struct iscsi_uevent) + sizeof(struct iscsi_path);
++	ubuf = calloc(1, NLMSG_SPACE(buflen));
++	if (!ubuf) {
++		LOG_ERR(PFX "%s: alloc failed for uevent buf", __func__);
++		return;
++	}
++
++	memset(ubuf, 0, NLMSG_SPACE(buflen));
++
++	/*  prepare the iscsi_uevent buffer */
++	ev = (struct iscsi_uevent *)ubuf;
++	ev->type = ISCSI_UEVENT_PATH_UPDATE;
++	ev->transport_handle = nic->transport_handle;
++	ev->u.set_path.host_no = nic->host_no;
++
++	/*  Prepare the iscsi_path buffer */
++	path_data = (struct iscsi_path *)(ubuf + sizeof(struct iscsi_uevent));
++	path_data->handle = QEDI_PATH_HANDLE;
++	path_data->vlan_id = vlan_id;
++	uctrl->host_tx_pkt_len = len;
++
++	rc = __kipc_call(nl_sock, ev, buflen);
++	if (rc > 0) {
++		bp->tx_prod++;
++		uctrl->host_tx_prod++;
++		msync(uctrl, sizeof(struct qedi_uio_ctrl), MS_SYNC);
++		LOG_PACKET(PFX "%s: sent %d bytes using bp->tx_prod: %d",
++			   nic->log_name, len, bp->tx_prod);
++	} else {
++		LOG_ERR(PFX "Pkt transmission failed: %d", rc);
++		pthread_mutex_unlock(&nic->xmit_mutex);
++	}
++
++	free(ubuf);
++}
++
++/**
++ *  qedi_write() - Used to write the data to the hardware
++ *  @param nic - NIC hardware to read from
++ *  @param pkt - The packet which will hold the data to be sent on the wire
++ *  @return 0 if successful, <0 if failed
++ */
++int qedi_write(nic_t *nic, nic_interface_t *nic_iface, packet_t *pkt)
++{
++	qedi_t *bp;
++	struct uip_stack *uip;
++	int i = 0;
++
++	/* Sanity Check: validate the parameters */
++	if (!nic || !nic_iface || !pkt) {
++		LOG_ERR(PFX "%s: qedi_write() nic == 0x%p || nic_iface == 0x%p || pkt == 0x%x",
++			nic, nic_iface, pkt);
++		return -EINVAL;
++	}
++	bp = (qedi_t *)nic->priv;
++	uip = &nic_iface->ustack;
++
++	if (pkt->buf_size == 0) {
++		LOG_ERR(PFX "%s: Trying to transmitted 0 sized packet",
++			nic->log_name);
++		return -EINVAL;
++	}
++
++	/*  Try to wait for a TX completion */
++	for (i = 0; i < 15; i++) {
++		struct timespec sleep_req = {.tv_sec = 0, .tv_nsec = 5000000 },
++		    sleep_rem;
++
++		if (qedi_clear_tx_intr(nic) == 0)
++			break;
++
++		nanosleep(&sleep_req, &sleep_rem);
++	}
++
++	if (pthread_mutex_trylock(&nic->xmit_mutex) != 0) {
++		LOG_PACKET(PFX "%s: Dropped previous transmitted packet",
++			   nic->log_name);
++		return -EINVAL;
++	}
++
++	qedi_prepare_xmit_packet(nic, nic_iface, pkt);
++	qedi_start_xmit(nic, pkt->buf_size,
++			(nic_iface->vlan_priority << 12) |
++			nic_iface->vlan_id);
++
++	/* bump up the tx stats */
++	nic->stats.tx.packets++;
++	nic->stats.tx.bytes += uip->uip_len;
++
++	LOG_PACKET(PFX "%s: transmitted %d bytes dev->tx_cons: %d, dev->tx_prod: %d, dev->tx_bd_prod:%d",
++		   nic->log_name, pkt->buf_size,
++		   bp->tx_cons, bp->tx_prod, bp->tx_bd_prod);
++
++	return 0;
++}
++
++/**
++ *  qedi_read() - Used to read the data from the hardware
++ *  @param nic - NIC hardware to read from
++ *  @param pkt - The packet which will hold the data
++ *  @return 0 if successful, < 0 if failed
++ */
++static int qedi_read(nic_t *nic, packet_t *pkt)
++{
++	qedi_t *bp;
++	void *rx_pkt;
++	int rc = 0;
++	uint32_t sw_cons, bd_cons;
++	uint32_t hw_prod;
++	uint32_t rx_pkt_idx;
++	int len;
++	struct qedi_rx_bd *rx_bd;
++	struct qedi_uio_ctrl *uctrl;
++	uint16_t vlan_id;
++
++	/* Sanity Check: validate the parameters */
++	if (!nic || !pkt) {
++		LOG_ERR(PFX "%s: qedi_read() nic == 0x%p || pkt == 0x%x",
++			nic, pkt);
++		return -EINVAL;
++	}
++
++	bp = (qedi_t *)nic->priv;
++	msync(bp->uctrl_map, sizeof(struct qedi_uio_ctrl), MS_SYNC);
++	msync(bp->rx_comp_ring, nic->page_size, MS_SYNC);
++	uctrl = (struct qedi_uio_ctrl *)bp->uctrl_map;
++	hw_prod = uctrl->hw_rx_prod;
++	sw_cons = uctrl->host_rx_cons;
++	bd_cons = uctrl->host_rx_bd_cons;
++	rx_bd = bp->rx_comp_ring + (bd_cons * sizeof(*rx_bd));
++	len = rx_bd->rx_pkt_len;
++	rx_pkt_idx = rx_bd->rx_pkt_index;
++	vlan_id = rx_bd->vlan_id;
++
++	if (sw_cons != hw_prod) {
++		LOG_DEBUG(PFX "%s: clearing rx interrupt: %d %d",
++			  nic->log_name, sw_cons, hw_prod);
++		rc = 1;
++		rx_pkt = bp->rx_pkts + (bp->rx_buffer_size * rx_pkt_idx);
++
++		if (len > 0) {
++			msync(rx_pkt, len, MS_SYNC);
++			/*  Copy the data */
++			memcpy(pkt->buf, rx_pkt, len);
++			pkt->buf_size = len;
++			if (vlan_id) {
++				pkt->vlan_tag = vlan_id;
++				pkt->flags |= VLAN_TAGGED;
++			} else {
++				pkt->vlan_tag = 0;
++			}
++
++			LOG_DEBUG(PFX "%s: processing packet length: %d",
++				  nic->log_name, len);
++
++			/* bump up the recv stats */
++			nic->stats.rx.packets++;
++			nic->stats.rx.bytes += pkt->buf_size;
++		} else {
++			rc = 0;
++		}
++
++		sw_cons = (sw_cons + 1) % RX_RING_SIZE;
++		bd_cons = (bd_cons + 1) % QEDI_NUM_RX_BD;
++		uctrl->host_rx_cons_cnt++;
++	}
++
++	uctrl->host_rx_bd_cons = bd_cons;
++	uctrl->host_rx_cons = sw_cons;
++
++	msync(uctrl, sizeof(struct qedi_uio_ctrl), MS_SYNC);
++	msync(bp->rx_comp_ring, nic->page_size, MS_SYNC);
++	return rc;
++}
++
++/*******************************************************************************
++ * Clearing TX interrupts
++ ******************************************************************************/
++/**
++ *  qedi_clear_tx_intr() - This routine is called when a TX interrupt occurs
++ *  @param nic - the nic the interrupt occurred on
++ *  @return  0 on success
++ */
++
++static int qedi_clear_tx_intr(nic_t *nic)
++{
++	qedi_t *bp;
++	uint32_t hw_cons;
++	struct qedi_uio_ctrl *uctrl;
++
++	/* Sanity check: ensure the parameters passed in are valid */
++	if (unlikely(!nic)) {
++		LOG_ERR(PFX "%s: nic == NULL", __func__);
++		return -EINVAL;
++	}
++
++	bp = (qedi_t *)nic->priv;
++	uctrl = (struct qedi_uio_ctrl *)bp->uctrl_map;
++	msync(bp->uctrl_map, sizeof(struct qedi_uio_ctrl), MS_SYNC);
++	hw_cons = uctrl->hw_tx_cons;
++
++	if (bp->tx_cons == hw_cons) {
++		if (bp->tx_cons == bp->tx_prod) {
++			/* Make sure the xmit_mutex lock is unlock */
++			if (pthread_mutex_trylock(&nic->xmit_mutex))
++				LOG_ERR(PFX "qedi tx lock with prod == cons");
++
++			pthread_mutex_unlock(&nic->xmit_mutex);
++			return 0;
++		}
++		return -EAGAIN;
++	}
++
++	LOG_PACKET(PFX "%s: clearing tx interrupt [%d %d]",
++		   nic->log_name, bp->tx_cons, hw_cons);
++	bp->tx_cons = hw_cons;
++
++	/* There is a queued TX packet that needs to be sent out.  The usual
++	 * case is when stack will send an ARP packet out before sending the
++	 * intended packet
++	 */
++	if (nic->tx_packet_queue) {
++		packet_t *pkt;
++		int i;
++
++		LOG_PACKET(PFX "%s: sending queued tx packet", nic->log_name);
++		pkt = nic_dequeue_tx_packet(nic);
++
++		/* Got a TX packet buffer of the TX queue and put it onto
++		 * the hardware
++		 */
++		if (pkt) {
++			qedi_prepare_xmit_packet(nic, pkt->nic_iface, pkt);
++
++			qedi_start_xmit(nic, pkt->buf_size,
++					(pkt->nic_iface->vlan_priority << 12) |
++					pkt->nic_iface->vlan_id);
++
++			LOG_PACKET(PFX "%s: transmitted queued packet %d bytes, dev->tx_cons: %d, dev->tx_prod: %d, dev->tx_bd_prod:%d",
++				   nic->log_name, pkt->buf_size,
++				   bp->tx_cons, bp->tx_prod, bp->tx_bd_prod);
++
++			return 0;
++		}
++
++		/* Try to wait for a TX completion */
++		for (i = 0; i < 15; i++) {
++			struct timespec sleep_req = {.tv_sec = 0,
++				.tv_nsec = 5000000
++			}, sleep_rem;
++
++			hw_cons = uctrl->hw_tx_cons;
++			if (bp->tx_cons != hw_cons) {
++				LOG_PACKET(PFX
++					   "%s: clearing tx interrupt [%d %d]",
++					   nic->log_name, bp->tx_cons, hw_cons);
++				bp->tx_cons = hw_cons;
++
++				break;
++			}
++
++			nanosleep(&sleep_req, &sleep_rem);
++		}
++	}
++
++	pthread_mutex_unlock(&nic->xmit_mutex);
++
++	return 0;
++}
++
++/*******************************************************************************
++ * qedi NIC op's table
++ ******************************************************************************/
++struct nic_ops qedi_op = {
++	.description = "qedi",
++	.open = qedi_open,
++	.close = qedi_close,
++	.write = qedi_write,
++	.get_tx_pkt = qedi_get_tx_pkt,
++	.start_xmit = qedi_start_xmit,
++	.read = qedi_read,
++	.clear_tx_intr = qedi_clear_tx_intr,
++	.handle_iscsi_path_req = cnic_handle_iscsi_path_req,
++
++	.lib_ops = {
++		    .get_library_name = qedi_get_library_name,
++		    .get_library_version = qedi_get_library_version,
++		    .get_build_date = qedi_get_build_date,
++		    .get_transport_name = qedi_get_transport_name,
++		    .get_uio_name = qedi_get_uio_name,
++		    },
++};
+diff --git a/iscsiuio/src/unix/libs/qedi.h b/iscsiuio/src/unix/libs/qedi.h
+new file mode 100644
+index 000000000000..7e0140adb2ba
+--- /dev/null
++++ b/iscsiuio/src/unix/libs/qedi.h
+@@ -0,0 +1,159 @@
++/*
++ * Copyright (c) 2016, Cavium Inc.
++ *
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ *    notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ *    notice, this list of conditions and the following disclaimer in the
++ *    documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ *    must display the following acknowledgement:
++ *      This product includes software developed by Adam Dunkels.
++ * 4. The name of the author may not be used to endorse or promote
++ *    products derived from this software without specific prior
++ *    written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
++ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
++ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
++ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ * qedi.h - qedi user space driver
++ *
++ */
++#ifndef __QEDI_H__
++#define __QEDI_H__
++
++#include "nic.h"
++
++#define RX_RING_SIZE	15
++#define PKT_BUF_SIZE 0X400
++#define QEDI_PAGE_SIZE 4096
++
++#define QEDI_UNKNOWN_MAJOR_VERSION	-1
++#define QEDI_UNKNOWN_MINOR_VERSION	-1
++#define QEDI_UNKNOWN_SUB_MINOR_VERSION	-1
++struct qedi_driver_version {
++	uint16_t major;
++	uint16_t minor;
++	uint16_t sub_minor;
++};
++
++#define QEDI_UCTRL_MAP_REG	0
++#define QEDI_RING_MAP_REG	1
++#define QEDI_BUF_MAP_REG	2
++#define UIO_ATTR_TMPL	"/sys/class/uio/uio%u/maps/map%u/%s"
++#define UIO_ADDR_TMPL	"/sys/class/uio/uio%u/maps/map%u/addr"
++#define UIO_OFFSET_TMPL	"/sys/class/uio/uio%u/maps/map%u/offset"
++#define UIO_SIZE_TMPL	"/sys/class/uio/uio%u/maps/map%u/size"
++
++struct qedi_uio_ctrl {
++	/* meta data */
++	__u32 uio_hsi_version;
++
++	/* user writes */
++	__u32 host_tx_prod;
++	__u32 host_rx_cons;
++	__u32 host_rx_bd_cons;
++	__u32 host_tx_pkt_len;
++	__u32 host_rx_cons_cnt;
++
++	/* driver writes */
++	__u32 hw_tx_cons;
++	__u32 hw_rx_prod;
++	__u32 hw_rx_bd_prod;
++	__u32 hw_rx_prod_cnt;
++
++	/* other */
++	__u8 mac_addr[6];
++	__u8 reserve[2];
++};
++
++struct qedi_rx_bd {
++	__u32 rx_pkt_index;
++	__u32 rx_pkt_len;
++	__u16 vlan_id;
++};
++
++#define QEDI_RX_DESC_CNT	(QEDI_PAGE_SIZE / sizeof(struct qedi_rx_bd))
++#define QEDI_MAX_RX_DESC_CNT	(QEDI_RX_DESC_CNT - 1)
++#define QEDI_NUM_RX_BD		(QEDI_RX_DESC_CNT * 1)
++#define QEDI_MAX_RX_BD		(QEDI_NUM_RX_BD - 1)
++
++#define QEDI_NEXT_RX_IDX(x)	((((x) & (QEDI_MAX_RX_DESC_CNT)) ==     \
++				  (QEDI_MAX_RX_DESC_CNT - 1)) ?         \
++				 (x) + 2 : (x) + 1)
++
++#define QEDI_PATH_HANDLE	0xFE0000000
++
++typedef struct qedi {
++	nic_t *parent;
++
++	struct qedi_driver_version version;
++
++	uint16_t flags;
++#define CNIC_UIO_UNITIALIZED		0x0001
++#define CNIC_UIO_INITIALIZED		0x0002
++#define CNIC_UIO_ENABLED		0x0004
++#define CNIC_UIO_DISABLED		0x0008
++#define CNIC_UIO_IPv6_ENABLED		0x0010
++#define CNIC_UIO_ADDED_MULICAST		0x0020
++#define CNIC_UIO_MSIX_ENABLED		0x0200
++#define CNIC_UIO_TX_HAS_SENT		0x0400
++#define QEDI_OPENED			0x0800
++
++	__u32 chip_id;
++	int func;
++	int port;
++	int pfid;
++	__u32 cid;
++	__u32 client_id;
++
++	__u32 tx_prod;
++	__u32 tx_bd_prod;
++	__u32 tx_cons;
++	__u8 tx_vlan_tag_bit;
++
++	__u32 rx_prod;
++	__u32 rx_bd_prod;
++	__u32 rx_cons;
++	__u32 rx_bd_cons;
++	__u32 rx_hw_prod;
++
++	 __u32 (*get_rx_cons)(struct qedi *);
++	 __u32 (*get_tx_cons)(struct qedi *);
++
++	/* RX ring parameters */
++	uint32_t rx_ring_size;
++	uint32_t rx_buffer_size;
++
++	void *bufs; /* Pointer to the mapped buffer space */
++	void *uctrl_map; /* UIO control structure */
++	uint32_t uctrl_map_offset; /* UIO control structure mmap offset */
++
++	uint32_t rx_index;
++	void *rx_comp_ring;
++	void **rx_pkt_ring;
++	void *tx_pkt;
++	void *rx_pkts;
++} qedi_t;
++
++/******************************************************************************
++ *  qedi Function Declarations
++ ******************************************************************************/
++void qedi_start_xmit(nic_t *nic, size_t len, u16_t vlan_id);
++struct nic_ops *qedi_get_ops();
++
++#endif /* __QEDI_H__ */
+diff --git a/iscsiuio/src/unix/nic.c b/iscsiuio/src/unix/nic.c
+index 8825f00b7216..a5f714a91923 100644
+--- a/iscsiuio/src/unix/nic.c
++++ b/iscsiuio/src/unix/nic.c
+@@ -65,6 +65,7 @@
+ 
+ #include "bnx2.h"
+ #include "bnx2x.h"
++#include "qedi.h"
+ #include "ipv6.h"
+ 
+ /******************************************************************************
+@@ -180,7 +181,7 @@ error:
+ }
+ 
+ static struct nic_ops *(*nic_get_ops[]) () = {
+-bnx2_get_ops, bnx2x_get_ops,};
++bnx2_get_ops, bnx2x_get_ops, qedi_get_ops};
+ 
+ int load_all_nic_libraries()
+ {
+@@ -404,6 +405,7 @@ nic_t *nic_init()
+ 	memset(nic, 0, sizeof(*nic));
+ 	nic->uio_minor = -1;
+ 	nic->fd = INVALID_FD;
++	nic->host_no = INVALID_HOST_NO;
+ 	nic->next = NULL;
+ 	nic->thread = INVALID_THREAD;
+ 	nic->enable_thread = INVALID_THREAD;
+@@ -1070,6 +1072,8 @@ int process_packets(nic_t *nic,
+ 		}
+ nic_iface_present:
+ 		pkt->nic_iface = nic_iface;
++		LOG_DEBUG(PFX "%s: found nic iface, type=0x%x, bufsize=%d",
++			  nic->log_name, type, pkt->buf_size);
+ 
+ 		ustack = &nic_iface->ustack;
+ 
+diff --git a/iscsiuio/src/unix/nic.h b/iscsiuio/src/unix/nic.h
+index ec157815a375..2c41e6e3c25a 100644
+--- a/iscsiuio/src/unix/nic.h
++++ b/iscsiuio/src/unix/nic.h
+@@ -334,6 +334,7 @@ typedef struct nic {
+ 
+ 	/* The thread used to perform ping */
+ 	pthread_t ping_thread;
++	uint64_t transport_handle;
+ } nic_t;
+ 
+ /******************************************************************************
+diff --git a/iscsiuio/src/unix/nic_utils.c b/iscsiuio/src/unix/nic_utils.c
+index 0daffd2fcb6d..6e580f862eea 100644
+--- a/iscsiuio/src/unix/nic_utils.c
++++ b/iscsiuio/src/unix/nic_utils.c
+@@ -81,6 +81,10 @@ static const char iscsi_host_path_netdev_template[] =
+ 	"/sys/class/iscsi_host/host%d/netdev";
+ static const char cnic_uio_sysfs_resc_template[] =
+ 	"/sys/class/uio/uio%i/device/resource%i";
++static const char iscsi_transport_handle_template[] =
++	"/sys/class/iscsi_transport/%s/handle";
++static const char eth_pfx[] = "eth";
++static const char host_pfx[] = "host";
+ 
+ /**
+  *  manually_trigger_uio_event() - If the uio file node doesn't exist then
+@@ -250,6 +254,7 @@ int nic_discover_iscsi_hosts()
+ 				strncpy(nic->eth_device_name, raw, raw_size);
+ 				nic->config_device_name = nic->eth_device_name;
+ 				nic->log_name = nic->eth_device_name;
++				nic->host_no = host_no;
+ 
+ 				if (nic_fill_name(nic) != 0) {
+ 					free(nic);
+@@ -557,6 +562,102 @@ error:
+ }
+ 
+ /**
++ *  from_uio_find_associated_host() - Given the uio minor number
++ *      this function will try to find the assoicated iscsi host
++ *  @param uio_minor - minor number of the UIO device
++ *  @param name - char buffer which will be filled if successful
++ *  @param name_size - size of the name buffer
++ *  @return >0 minor number <0 an error
++ */
++static int from_uio_find_associated_host(nic_t *nic, int uio_minor,
++					 char *name, size_t name_size)
++{
++	char *path;
++	int rc;
++	int count;
++	struct dirent **files;
++	char *parsed_name;
++	int i;
++	int path_iterator;
++	char *search_paths[] = { "/sys/class/uio/uio%i/device/" };
++	int path_to[] = { 5, 1 };
++	int (*search_filters[]) (const struct dirent *) = { filter_host_name, };
++	char *(*extract_name[]) (struct dirent **files) = {  extract_none, };
++	int extract_name_offset[] = { 0 };
++
++	path = malloc(PATH_MAX);
++	if (!path) {
++		LOG_ERR(PFX "Could not allocate memory for path");
++		rc = -ENOMEM;
++		goto error;
++	}
++
++	for (path_iterator = 0;
++	     path_iterator < sizeof(search_paths) / sizeof(search_paths[0]);
++	     path_iterator++) {
++		/*  Build the path to determine uio name */
++		rc = sprintf(path, search_paths[path_iterator], uio_minor);
++
++		wait_for_file_node_timed(nic, path, path_to[path_iterator]);
++
++		count = scandir(path, &files,
++				search_filters[path_iterator], alphasort);
++
++		switch (count) {
++		case 1:
++			parsed_name = (*extract_name[path_iterator]) (files);
++			if (!parsed_name) {
++				LOG_WARN(PFX "Couldn't find delimiter in: %s",
++					 files[0]->d_name);
++
++				break;
++			}
++
++			strncpy(name,
++				parsed_name +
++				extract_name_offset[path_iterator], name_size);
++
++			free(files[0]);
++			free(files);
++
++			rc = 0;
++			break;
++
++		case 0:
++			rc = -EINVAL;
++			break;
++
++		case -1:
++			LOG_WARN(PFX "Error when scanning path: %s[%s]",
++				 path, strerror(errno));
++			rc = -EINVAL;
++			break;
++
++		default:
++			LOG_WARN(PFX
++				 "Too many entries when looking for device: %s",
++				 path);
++
++			/*  Cleanup the scandir() call */
++			for (i = 0; i < count; i++)
++				free(files[i]);
++			free(files);
++
++			rc = -EINVAL;
++			break;
++		}
++
++		if (rc == 0)
++			break;
++	}
++
++error:
++	free(path);
++
++	return rc;
++}
++
++/**
+  *  filter_uio_name() - This is the callback used by scandir when looking for
+  *                      the number of uio entries
+  */
+@@ -652,10 +753,16 @@ int from_phys_name_find_assoicated_uio_device(nic_t *nic)
+ 			continue;
+ 		}
+ 
+-		rc = from_uio_find_associated_eth_device(nic,
+-							 uio_minor,
+-							 eth_name,
+-							 sizeof(eth_name));
++		if (!memcmp(host_pfx, nic->config_device_name,
++			    strlen(host_pfx))) {
++			rc = from_uio_find_associated_host(nic, uio_minor,
++							   eth_name,
++							   sizeof(eth_name));
++		} else {
++			rc = from_uio_find_associated_eth_device(nic, uio_minor,
++								 eth_name,
++								 sizeof(eth_name));
++		}
+ 		if (rc != 0) {
+ 			LOG_WARN("uio minor: %d not valid [%D]", uio_minor, rc);
+ 			continue;
+@@ -1124,6 +1231,38 @@ int detemine_initial_uio_events(nic_t *nic, uint32_t *num_of_events)
+ 
+ 	rc = 0;
+ error:
++	if (raw)
++		free(raw);
++
++	return rc;
++}
++
++int get_iscsi_transport_handle(nic_t *nic, uint64_t *handle)
++{
++	char *raw = NULL;
++	uint32_t raw_size = 0;
++	ssize_t elements_read;
++	char temp_path[sizeof(iscsi_transport_handle_template) + 8];
++	int rc;
++
++	/*  Capture RX buffer size */
++	snprintf(temp_path, sizeof(temp_path),
++		 iscsi_transport_handle_template, nic->library_name);
++
++	rc = capture_file(&raw, &raw_size, temp_path);
++	if (rc != 0)
++		goto error;
++
++	elements_read = sscanf(raw, "%lu", handle);
++	if (elements_read != 1) {
++		LOG_ERR(PFX "%s: Couldn't parse transport handle from %s",
++			nic->log_name, temp_path);
++		rc = -EIO;
++		goto error;
++	}
++
++	rc = 0;
++error:
+ 	if (raw != NULL)
+ 		free(raw);
+ 
+diff --git a/iscsiuio/src/unix/nic_utils.h b/iscsiuio/src/unix/nic_utils.h
+index d5c1b580f340..e4cf5c13177e 100644
+--- a/iscsiuio/src/unix/nic_utils.h
++++ b/iscsiuio/src/unix/nic_utils.h
+@@ -99,4 +99,6 @@ void dump_packet_to_log(struct nic_interface *iface,
+ int determine_file_size_read(const char *filepath);
+ int capture_file(char **raw, uint32_t *raw_size, const char *path);
+ 
++int get_iscsi_transport_handle(nic_t *nic, uint64_t *handle);
++
+ #endif /* __NIC_UTILS_H__ */
+diff --git a/iscsiuio/src/unix/options.h b/iscsiuio/src/unix/options.h
+index df03255f7a87..63b86357aafa 100644
+--- a/iscsiuio/src/unix/options.h
++++ b/iscsiuio/src/unix/options.h
+@@ -87,6 +87,7 @@
+ 
+ #define INVALID_FD	-1
+ #define INVALID_THREAD	-1
++#define INVALID_HOST_NO	-1
+ 
+ struct options {
+ 	char debug;
+-- 
+2.9.3
+
diff --git a/SOURCES/open-iscsi-2.0.874-9-iscsiuio-v0.7.8.3.patch b/SOURCES/open-iscsi-2.0.874-9-iscsiuio-v0.7.8.3.patch
new file mode 100644
index 0000000..ec17af9
--- /dev/null
+++ b/SOURCES/open-iscsi-2.0.874-9-iscsiuio-v0.7.8.3.patch
@@ -0,0 +1,84 @@
+From 13ade0423449530836ccb4c4d94ae36ce5257b56 Mon Sep 17 00:00:00 2001
+From: Nilesh Javali <nilesh.javali@cavium.com>
+Date: Fri, 11 Nov 2016 08:17:52 +0200
+Subject: iscsiuio: v0.7.8.3
+
+Signed-off-by: Manish Rangankar <manish.rangankar@cavium.com>
+Signed-off-by: Adheer Chandravanshi <adheer.chandravanshi@qlogic.com>
+Signed-off-by: Nilesh Javali <nilesh.javali@cavium.com>
+---
+ iscsiuio/README       |  4 ++--
+ iscsiuio/RELEASE.TXT  | 20 ++++++++------------
+ iscsiuio/configure.ac |  4 ++--
+ 3 files changed, 12 insertions(+), 16 deletions(-)
+
+diff --git a/iscsiuio/README b/iscsiuio/README
+index 9ae14111dd40..ca2da16a7342 100644
+--- a/iscsiuio/README
++++ b/iscsiuio/README
+@@ -1,6 +1,6 @@
+ Iscsiuio Userspace Tool
+-Version 0.7.8.2
+-Dec 10, 2013
++Version 0.7.8.3
++Sept 28, 2016
+ ------------------------------------------------------
+ 
+ This tool is to be used in conjunction with the QLogic NetXtreme II Linux
+diff --git a/iscsiuio/RELEASE.TXT b/iscsiuio/RELEASE.TXT
+index 44d67f9ceb84..69c5b5f6bd61 100644
+--- a/iscsiuio/RELEASE.TXT
++++ b/iscsiuio/RELEASE.TXT
+@@ -1,7 +1,7 @@
+                               Release Notes
+                         QLogic uIP Linux Driver
+-                            Version 0.7.8.2
+-                               12/10/2013
++                            Version 0.7.8.3
++                               9/28/2016
+ 
+                           QLogic Corporation
+                         26650 Aliso Viejo Pkwy,
+@@ -11,17 +11,13 @@
+                    Copyright (c) 2014, QLogic Corporation
+                            All rights reserved
+ 
+-uIP v0.7.10.2 (Feb 12, 2014)
+-=======================================================
+-   Fixes
+-   -----
+-   1. Problem: Cont00072504 - ifconfig shows allocation failure after
+-               up/down few hours with iSCSI + L2 traffic
+-      Cause:   A memory leak was discovered in the ongoing pthread creation
+-               destruction code during the connection recovery process
+-      Change:  Fixed the pthread creation code
+-      Impact:  All
+ 
++uIP v0.7.8.3 (Sept 28, 2016)
++=======================================================
++   Enhancements
++   ------------
++   1. Change: Add support for the new qedi transport
++      Impact: 10/25/40/50GGbE Controller (iSCSI)
+ 
+ uIP v0.7.8.2 (Dec 10, 2013)
+ =======================================================
+diff --git a/iscsiuio/configure.ac b/iscsiuio/configure.ac
+index ed1499cdbba1..075d07d04f34 100644
+--- a/iscsiuio/configure.ac
++++ b/iscsiuio/configure.ac
+@@ -12,9 +12,9 @@ dnl             Benjamin Li  (benli@broadcom.com)
+ dnl
+ 
+ PACKAGE=iscsiuio
+-VERSION=0.7.8.2
++VERSION=0.7.8.3
+ 
+-AC_INIT([iscsiuio], [0.7.8.2], [QLogic-Storage-Upstream@qlogic.com])
++AC_INIT([iscsiuio], [0.7.8.3], [QLogic-Storage-Upstream@cavium.com])
+ 
+ AM_INIT_AUTOMAKE
+ AC_CONFIG_HEADER(config.h)
+-- 
+2.9.3
+
diff --git a/SOURCES/open-iscsi-2.0.874-iscsid-reset-head-on-wrap-when-buffer-empty.patch b/SOURCES/open-iscsi-2.0.874-iscsid-reset-head-on-wrap-when-buffer-empty.patch
new file mode 100644
index 0000000..5925cec
--- /dev/null
+++ b/SOURCES/open-iscsi-2.0.874-iscsid-reset-head-on-wrap-when-buffer-empty.patch
@@ -0,0 +1,27 @@
+From 2235c48bd993ead1d6e3de405b98b524d4bc0b61 Mon Sep 17 00:00:00 2001
+From: Chris Leech <cleech@redhat.com>
+Date: Tue, 28 Feb 2017 19:34:03 -0800
+Subject: iscsid: reset head on wrap when buffer empty
+
+Reported-By: David Jeffery <djeffery@redhat.com>
+---
+ usr/log.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/usr/log.c b/usr/log.c
+index 26c61d847793..b730642779bf 100644
+--- a/usr/log.c
++++ b/usr/log.c
+@@ -189,6 +189,9 @@ int log_enqueue (int prio, const char * fmt, va_list ap)
+ 	    (len + sizeof(struct logmsg)) > (la->end - la->tail)) {
+ 		logdbg(stderr, "enqueue: rewind tail to %p\n", la->tail);
+ 			la->tail = la->start;
++
++			if (la->empty)
++				la->head = lastmsg = la->tail;
+ 	}
+ 
+ 	/* not enough space on head : drop msg */
+-- 
+2.9.3
+
diff --git a/SPECS/iscsi-initiator-utils.spec b/SPECS/iscsi-initiator-utils.spec
new file mode 100644
index 0000000..a7bd696
--- /dev/null
+++ b/SPECS/iscsi-initiator-utils.spec
@@ -0,0 +1,485 @@
+%define open_iscsi_version	2.0
+%define open_iscsi_build	874
+
+Summary: iSCSI daemon and utility programs
+Name: iscsi-initiator-utils
+Version: 6.%{open_iscsi_version}.%{open_iscsi_build}
+Release: 7%{?dist}
+Group: System Environment/Daemons
+License: GPLv2+
+URL: http://www.open-iscsi.org
+
+Source0: https://github.com/open-iscsi/open-iscsi/archive/%{open_iscsi_version}.%{open_iscsi_build}.tar.gz#/open-iscsi-%{open_iscsi_version}.%{open_iscsi_build}.tar.gz
+Source4: 04-iscsi
+Source5: iscsi-tmpfiles.conf
+
+# upstream patches, post last tagged version
+Patch1: open-iscsi-2.0.874-1-iBFT-origin-is-an-enum-not-a-string.patch
+Patch2: open-iscsi-2.0.874-4-iscsid-treat-SIGTERM-like-iscsiadm-k-0.patch
+Patch3: open-iscsi-2.0.874-5-Make-event_loop_stop-volatile-for-safer-access.patch
+Patch4: open-iscsi-2.0.874-7-iscsid-Changes-to-support-the-new-qedi-transport.patch
+Patch5: open-iscsi-2.0.874-8-iscsiuio-Add-support-for-the-new-qedi-transport.patch
+Patch6: open-iscsi-2.0.874-9-iscsiuio-v0.7.8.3.patch
+Patch7: open-iscsi-2.0.874-7-Allow-disabling-auto-LUN-scans.patch
+Patch8: open-iscsi-2.0.874-23-Fix-manual-LUN-scans-feature.patch
+Patch9: open-iscsi-2.0.874-27-iscsid-Add-qedi-ping-transport-hook.patch
+Patch20: open-iscsi-2.0.874-30-isolate-iscsistart-socket-use.patch
+# not (yet) upstream merged
+Patch140: open-iscsi-2.0.874-iscsid-reset-head-on-wrap-when-buffer-empty.patch
+Patch143: 0143-idmb_rec_write-check-for-tpgt-first.patch
+Patch145: 0145-idbm_rec_write-seperate-old-and-new-style-writes.patch
+Patch146: 0146-idbw_rec_write-pick-tpgt-from-existing-record.patch
+Patch149: 0149-update-systemd-service-files-add-iscsi.service-for-s.patch
+Patch150: 0150-iscsi-boot-related-service-file-updates.patch
+# distro specific modifications
+Patch151: 0151-update-initscripts-and-docs.patch
+Patch152: 0152-use-var-for-config.patch
+Patch153: 0153-use-red-hat-for-name.patch
+Patch154: 0154-add-libiscsi.patch
+Patch156: 0156-remove-the-offload-boot-supported-ifdef.patch
+Patch159: 0159-iscsiuio-systemd-unit-files.patch
+Patch160: 0160-use-systemctl-to-start-iscsid.patch
+Patch161: 0161-resolve-565245-multilib-issues-caused-by-doxygen.patch
+Patch162: 0162-Don-t-check-for-autostart-sessions-if-iscsi-is-not-u.patch
+Patch164: 0164-libiscsi-fix-incorrect-strncpy-use.patch
+Patch166: 0166-start-socket-listeners-on-iscsiadm-command.patch
+Patch167: 0167-Revert-iscsiadm-return-error-when-login-fails.patch
+Patch168: 0168-update-handling-of-boot-sessions.patch
+Patch169: 0169-update-iscsi.service-for-boot-session-recovery.patch
+Patch170: 0170-fix-systemd-unit-wants.patch
+Patch172: 0172-move-cleanup-to-seperate-service.patch
+Patch175: be2iscsi-vlan.patch
+# upstream removed internal open-isns, but not taking that here just yet
+# it requires repackaging isns-utils to provide a debug package
+Patch198: keep-open-isns.patch
+# version string, needs to be updated with each build
+Patch199: 0199-use-Red-Hat-version-string-to-match-RPM-package-vers.patch
+
+BuildRequires: flex bison python-devel doxygen kmod-devel systemd-devel libmount-devel autoconf automake libtool
+# For dir ownership
+Requires: %{name}-iscsiuio >= %{version}-%{release}
+Requires(post): systemd
+Requires(preun): systemd
+Requires(postun): systemd
+
+%global _hardened_build 1
+%global __provides_exclude_from ^(%{python_sitearch}/.*\\.so)$
+
+%description
+The iscsi package provides the server daemon for the iSCSI protocol,
+as well as the utility programs used to manage it. iSCSI is a protocol
+for distributed disk access using SCSI commands sent over Internet
+Protocol networks.
+
+%package iscsiuio
+Summary: Userspace configuration daemon required for some iSCSI hardware
+Group: System Environment/Daemons
+License: BSD
+Requires: %{name} = %{version}-%{release}
+
+%description iscsiuio
+The iscsiuio configuration daemon provides network configuration help
+for some iSCSI offload hardware.
+
+%package devel
+Summary: Development files for %{name}
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+The %{name}-devel package contains libraries and header files for
+developing applications that use %{name}.
+
+%prep
+%autosetup -p1 -n open-iscsi-%{open_iscsi_version}.%{open_iscsi_build}
+
+# change exec_prefix, there's no easy way to override
+%{__sed} -i -e 's|^exec_prefix = /$|exec_prefix = %{_exec_prefix}|' Makefile
+
+%build
+
+# configure sub-packages from here
+# letting the top level Makefile do it will lose setting from rpm
+cd iscsiuio
+autoreconf --install
+%{configure}
+cd ..
+
+cd utils/open-isns
+chmod +x ./configure
+%{configure} --with-security=no --with-slp=no
+cd ../..
+
+%{__make} OPTFLAGS="%{optflags} %{?__global_ldflags} -DUSE_KMOD -lkmod"
+pushd libiscsi
+python setup.py build
+touch -r libiscsi.doxy html/*
+popd
+
+
+%install
+%{__make} DESTDIR=%{?buildroot} install_programs install_doc install_etc
+# upstream makefile doesn't get everything the way we like it
+rm $RPM_BUILD_ROOT%{_sbindir}/iscsi_discovery
+rm $RPM_BUILD_ROOT%{_mandir}/man8/iscsi_discovery.8
+%{__install} -pm 755 usr/iscsistart $RPM_BUILD_ROOT%{_sbindir}
+%{__install} -pm 644 doc/iscsistart.8 $RPM_BUILD_ROOT%{_mandir}/man8
+%{__install} -pm 644 doc/iscsi-iname.8 $RPM_BUILD_ROOT%{_mandir}/man8
+%{__install} -d $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d
+%{__install} -pm 644 iscsiuio/iscsiuiolog $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d
+
+%{__install} -d $RPM_BUILD_ROOT%{_sharedstatedir}/iscsi
+%{__install} -d $RPM_BUILD_ROOT%{_sharedstatedir}/iscsi/nodes
+%{__install} -d $RPM_BUILD_ROOT%{_sharedstatedir}/iscsi/send_targets
+%{__install} -d $RPM_BUILD_ROOT%{_sharedstatedir}/iscsi/static
+%{__install} -d $RPM_BUILD_ROOT%{_sharedstatedir}/iscsi/isns
+%{__install} -d $RPM_BUILD_ROOT%{_sharedstatedir}/iscsi/slp
+%{__install} -d $RPM_BUILD_ROOT%{_sharedstatedir}/iscsi/ifaces
+
+# for %%ghost
+%{__install} -d $RPM_BUILD_ROOT/var/lock/iscsi
+touch $RPM_BUILD_ROOT/var/lock/iscsi/lock
+
+
+%{__install} -d $RPM_BUILD_ROOT%{_unitdir}
+%{__install} -pm 644 etc/systemd/iscsi.service $RPM_BUILD_ROOT%{_unitdir}
+%{__install} -pm 644 etc/systemd/iscsi-shutdown.service $RPM_BUILD_ROOT%{_unitdir}
+%{__install} -pm 644 etc/systemd/iscsid.service $RPM_BUILD_ROOT%{_unitdir}
+%{__install} -pm 644 etc/systemd/iscsid.socket $RPM_BUILD_ROOT%{_unitdir}
+%{__install} -pm 644 etc/systemd/iscsiuio.service $RPM_BUILD_ROOT%{_unitdir}
+%{__install} -pm 644 etc/systemd/iscsiuio.socket $RPM_BUILD_ROOT%{_unitdir}
+
+%{__install} -d $RPM_BUILD_ROOT%{_libexecdir}
+%{__install} -pm 755 etc/systemd/iscsi-mark-root-nodes $RPM_BUILD_ROOT%{_libexecdir}
+
+%{__install} -d $RPM_BUILD_ROOT%{_sysconfdir}/NetworkManager/dispatcher.d
+%{__install} -pm 755 %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir}/NetworkManager/dispatcher.d
+
+%{__install} -d $RPM_BUILD_ROOT%{_tmpfilesdir}
+%{__install} -pm 644 %{SOURCE5} $RPM_BUILD_ROOT%{_tmpfilesdir}/iscsi.conf
+
+%{__install} -d $RPM_BUILD_ROOT%{_libdir}
+%{__install} -pm 755 libiscsi/libiscsi.so.0 $RPM_BUILD_ROOT%{_libdir}
+%{__ln_s}    libiscsi.so.0 $RPM_BUILD_ROOT%{_libdir}/libiscsi.so
+%{__install} -d $RPM_BUILD_ROOT%{_includedir}
+%{__install} -pm 644 libiscsi/libiscsi.h $RPM_BUILD_ROOT%{_includedir}
+
+%{__install} -d $RPM_BUILD_ROOT%{python_sitearch}
+%{__install} -pm 755 libiscsi/build/lib.linux-*/libiscsimodule.so \
+	$RPM_BUILD_ROOT%{python_sitearch}
+
+
+%post
+/sbin/ldconfig
+
+%systemd_post iscsi.service iscsi-shutdown.service iscsid.service iscsid.socket
+
+if [ $1 -eq 1 ]; then
+	if [ ! -f %{_sysconfdir}/iscsi/initiatorname.iscsi ]; then
+		echo "InitiatorName=`/usr/sbin/iscsi-iname`" > %{_sysconfdir}/iscsi/initiatorname.iscsi
+	fi
+	# enable socket activation and persistant session startup by default
+	/bin/systemctl enable iscsi.service >/dev/null 2>&1 || :
+	/bin/systemctl enable iscsid.socket >/dev/null 2>&1 || :
+	/bin/systemctl start iscsid.socket >/dev/null 2>&1 || :
+fi
+
+%post iscsiuio
+%systemd_post iscsiuio.service iscsiuio.socket
+
+if [ $1 -eq 1 ]; then
+	/bin/systemctl enable iscsiuio.socket >/dev/null 2>&1 || :
+	/bin/systemctl start iscsiuio.socket >/dev/null 2>&1 || :
+fi
+
+%preun
+%systemd_preun iscsi.service iscsi-shutdown.service iscsid.service iscsiuio.service iscsid.socket iscsiuio.socket
+
+%preun iscsiuio
+%systemd_preun iscsiuio.service iscsiuio.socket
+
+%postun
+/sbin/ldconfig
+%systemd_postun
+
+%postun iscsiuio
+%systemd_postun
+
+%triggerun -- iscsi-initiator-utils < 6.2.0.873-22
+# prior to 6.2.0.873-22 iscsi.service was missing a Wants=remote-fs-pre.target
+# this forces remote-fs-pre.target active if needed for a clean shutdown/reboot
+# after upgrading this package
+if [ $1 -gt 0 ]; then
+    /usr/bin/systemctl -q is-active iscsi.service
+    if [ $? -eq 0 ]; then
+        /usr/bin/systemctl -q is-active remote-fs-pre.target
+        if [ $? -ne 0 ]; then
+            SRC=`/usr/bin/systemctl show --property FragmentPath remote-fs-pre.target | cut -d= -f2`
+            DST=/run/systemd/system/remote-fs-pre.target
+            if [ $SRC != $DST ]; then
+                cp $SRC $DST
+            fi
+            sed -i 's/RefuseManualStart=yes/RefuseManualStart=no/' $DST
+            /usr/bin/systemctl daemon-reload >/dev/null 2>&1 || :
+            /usr/bin/systemctl start remote-fs-pre.target >/dev/null 2>&1 || :
+        fi
+    fi
+fi
+# added in 6.2.0.873-26
+if [ $1 -gt 0 ]; then
+    systemctl start iscsi-shutdown.service >/dev/null 2>&1 || :
+fi
+
+%files
+%doc README
+%dir %{_sharedstatedir}/iscsi
+%dir %{_sharedstatedir}/iscsi/nodes
+%dir %{_sharedstatedir}/iscsi/isns
+%dir %{_sharedstatedir}/iscsi/static
+%dir %{_sharedstatedir}/iscsi/slp
+%dir %{_sharedstatedir}/iscsi/ifaces
+%dir %{_sharedstatedir}/iscsi/send_targets
+%ghost %{_var}/lock/iscsi
+%ghost %{_var}/lock/iscsi/lock
+%{_unitdir}/iscsi.service
+%{_unitdir}/iscsi-shutdown.service
+%{_unitdir}/iscsid.service
+%{_unitdir}/iscsid.socket
+%{_libexecdir}/iscsi-mark-root-nodes
+%{_sysconfdir}/NetworkManager/dispatcher.d/04-iscsi
+%{_tmpfilesdir}/iscsi.conf
+%dir %{_sysconfdir}/iscsi
+%attr(0600,root,root) %config(noreplace) %{_sysconfdir}/iscsi/iscsid.conf
+%{_sbindir}/iscsi-iname
+%{_sbindir}/iscsiadm
+%{_sbindir}/iscsid
+%{_sbindir}/iscsistart
+%{_libdir}/libiscsi.so.0
+%{python_sitearch}/libiscsimodule.so
+%{_mandir}/man8/iscsi-iname.8.gz
+%{_mandir}/man8/iscsiadm.8.gz
+%{_mandir}/man8/iscsid.8.gz
+%{_mandir}/man8/iscsistart.8.gz
+
+%files iscsiuio
+%{_sbindir}/iscsiuio
+%{_unitdir}/iscsiuio.service
+%{_unitdir}/iscsiuio.socket
+%config(noreplace) %{_sysconfdir}/logrotate.d/iscsiuiolog
+%{_mandir}/man8/iscsiuio.8.gz
+
+%files devel
+%doc libiscsi/html
+%{_libdir}/libiscsi.so
+%{_includedir}/libiscsi.h
+
+%changelog
+* Thu Nov 30 2017 Chris Leech <cleech@redhat.com> - 6.2.0.874-7
+- 1328694 keep vlan settings in sync for ipv4/ipv6 iface records with be2iscsi
+
+* Wed Nov 01 2017 Chris Leech <cleech@redhat.com> - 6.2.0.874-6
+- 1507945 force start iscsiuio for boot session recovery with qedi
+- 1457359 start systemd socket listeners, otherwise if iscsid is started
+  directly iscsiuio doesn't activate as expected
+
+* Tue Aug 15 2017 Chris Leech <cleech@redhat.com> - 6.2.0.874-5
+- 1431622 fix default in iscsi-iname manpage to match Red Hat customization
+
+* Tue Jun 27 2017 Chris Leech <cleech@redhat.com> - 6.2.0.874-4
+- 1450756 isolate iscsistart sockets
+
+* Fri Apr 28 2017 Chris Leech <cleech@redhat.com> - 6.2.0.874-3
+- 1445686 add missing ping hook for the qedi transport driver
+
+* Tue Apr 11 2017 Chris Leech <cleech@redhat.com> - 6.2.0.874-2
+- 1422941 allow disabling of auto scanning sessions, requested for OpenStack
+
+* Tue Feb 28 2017 Chris Leech <cleech@redhat.com> - 6.2.0.874-1
+- 1384090 upstream 2.0.874+ with qedi support
+- 1414819 iscsid reporting blank emerg messages
+
+* Thu Aug 18 2016 Chris Leech <cleech@redhat.com> - 6.2.0.873-35
+- 1362590 Revert iscsiuio pthread changes that result in a race condition on shutdown
+
+* Tue Jun 14 2016 Chris Leech <cleech@redhat.com> - 6.2.0.873-34
+- 1322000 ensure TCP abort on session failure to prevent data corruption with link flap
+- 1294964, 1265073, 1213569 iscsiuio update, fix small ARP table issue
+- 1309488 remove broken sysfs cache code to speed up login of many sessions
+- 1330348 sync with upstream Open-iSCSI for minor fixes
+
+* Tue Apr 26 2016 Chris Leech <cleech@redhat.com> - 6.2.0.873-33
+- 1275139 iscsiuio support for multi-function mode NetXtreme2 HBAs
+
+* Fri Jul 24 2015 Chris Leech <cleech@redhat.com> - 6.2.0.873-32
+- 1235684 apply safe_logout setting to flashnode sessions as well
+  but only when logging out by session id, not by flashnode index
+
+* Tue Jul 21 2015 Chris Leech <cleech@redhat.com> - 6.2.0.873-31
+- 1235684 fix safe logout DM name canonicalization, use libmount cache
+
+* Mon Jul 06 2015 Chris Leech <cleech@redhat.com> - 6.2.0.873-30
+- 1235684 add iscsid safe logout option
+
+* Fri Jan 30 2015 Chris Leech <cleech@redhat.com> - 6.2.0.873-29
+- 1166713 1187792 add missing ExecStart, only newer systemd lets that be optional for oneshot services
+
+* Thu Jan 15 2015 Chris Leech <cleech@redhat.com> - 6.2.0.873-28
+- 1180100 scriptlets were never split out properly for the iscsiuio subpackage
+
+* Thu Jan 15 2015 Chris Leech <cleech@redhat.com> - 6.2.0.873-27
+- 1168556 fix regression in network interface binding
+
+* Mon Jan 12 2015 Chris Leech <cleech@redhat.com> - 6.2.0.873-26
+- 1166713 created iscsi-shutdown.service to ensure that session cleanup happens
+
+* Thu Dec 11 2014 Andy Grover <agrover@redhat.com> - 6.2.0.873-25
+- Add --with-slp=no for #1088020
+
+* Tue Nov 18 2014 Chris Leech <cleech@redhat.com> - 6.2.0.873-24
+- 1040343 segfault from unexpected netlink event during discovery
+- inhibit strict aliasing optimizations in iscsiuio, rpmdiff error
+
+* Tue Oct 21 2014 Chris Leech <cleech@redhat.com> - 6.2.0.873-23
+- make sure to pass --with-security=no to isns configure (#1088020)
+
+* Wed Sep 24 2014 Chris Leech <cleech@redhat.com> - 6.2.0.873-22
+- 1081798 retry login on host not found error
+- 1111925 ignore iscsiadm return in iscsi.service
+- 1126524 make sure systemd order against remote mounts is correct
+- 963039 add discovery as a valid mode in iscsiadm.8
+- sync with upstream
+
+* Tue Mar 18 2014 Chris Leech <cleech@redhat.com> - 6.2.0.873-21
+- 1069825
+- boot session handling improvements
+- Fix iscsi-mark-root for changed iscsiadm output
+- Make sure iscsiuio is running for boot session recovery when using the
+  bnx2i transport by forcing iscsiuio.service start
+- Make NM dispatch triggered re-check for autostart sessions async
+- Accept exit code 21, no records, from iscsiadm as success in
+  iscsi.service
+
+* Tue Feb 25 2014 Chris Leech <cleech@redhat.com> - 6.2.0.873-20
+- 1049710 host0 being treated as an invalid in the host stats command
+- 1015563 revert change to return code when calling login_portal for sessions
+  that already exist, as it impacts users scripting around iscsiadm
+
+* Mon Feb 17 2014 Chris Leech <cleech@redhat.com> - 6.2.0.873-19
+- 1007388 fixes for iscsiadm to support qla4xxx
+- refresh boot session info patches to final version from upstream,
+  fixes context issues with later patches
+- 1006156, 1006161 Add/Update entries in chap table through Open-iSCSI
+- 948134 extend support to set additional parameters for network configuration
+- 1049710 update open-iscsi to support host statistics
+- 1043019 iscsiuio fix for arp cache flush issue
+- 1059332 Fix broken discovery sessions over iser
+- 1017393 split out iscsiuio into a seperate sub-package
+
+* Fri Jan 24 2014 Daniel Mach <dmach@redhat.com> - 6.2.0.873-18
+- Mass rebuild 2014-01-24
+
+* Fri Dec 27 2013 Daniel Mach <dmach@redhat.com> - 6.2.0.873-17
+- Mass rebuild 2013-12-27
+
+* Mon Nov 25 2013 Chris Leech <cleech@redhat.com> - 6.2.0.873-16
+- fix iscsiuio socket activation
+- have systemd start socket units on iscsiadm use, if not already listening
+
+* Sun Sep 15 2013 Chris Leech <cleech@redhat.com> - 6.2.0.873-15
+- move /sbin to /usr/sbin
+- use rpm macros in install rules
+
+* Fri Sep 13 2013 Chris Leech <cleech@redhat.com> - 6.2.0.873-14
+- fix iscsiuio hardened build and other compiler flags
+
+* Fri Aug 23 2013 Andy Grover <agrover@redhat.com> - 6.2.0.873-13
+- Fix patch 0041 to check session != NULL before calling iscsi_sysfs_read_boot()
+
+* Tue Aug 20 2013 Chris Leech <cleech@redhat.com> - 6.2.0.873-12
+- fix regression in last build, database records can't be accessed
+
+* Mon Aug 19 2013 Chris Leech <cleech@redhat.com> - 6.2.0.873-11
+- iscsi boot related fixes
+  make sure iscsid gets started if there are any boot sessions running
+  add reload target to fix double session problem when restarting from NM
+  don't rely on session list passed from initrd, never got fully implemented
+  remove patches related to running iscsid from initrd, possible to revisit later
+
+* Sun Aug 18 2013 Chris Leech <cleech@redhat.com> - 6.2.0.873-10
+- sync with upstream git, minor context fixes after rebase of out-of-tree patches
+- iscsiuio is merged upstream, remove old source archive and patches
+- spec cleanups to fix rpmlint issues
+
+* Sun Aug  4 2013 Peter Robinson <pbrobinson@fedoraproject.org> 6.2.0.873-9
+- Fix FTBFS, cleanup spec
+
+* Sat Aug 03 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 6.2.0.873-8
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
+
+* Tue Jun 11 2013 Chris Leech <cleech@redhat.com> - 6.2.0.873-7
+- Use the systemd tmpfiles service to recreate lockfiles in /var/lock
+- 955167 build as a position independent executable
+- 894576 fix order of setuid/setgid and drop additional groups
+
+* Tue May 28 2013 Chris Leech <cleech@redhat.com> - 6.2.0.873-6
+- Don't have iscsiadm scan for autostart record if node db is empty (bug #951951)
+
+* Tue Apr 30 2013 Orion Poplawski <orion@cora.nwra.com> - 6.2.0.873-5
+- Fix typo in NM dispatcher script (bug #917058)
+
+* Thu Feb 21 2013 Chris Leech <cleech@redhat.com> - 6.2.0.873-4
+- build with libkmod support, instead of calling out to modprobe
+- enable socket activation by default
+
+* Thu Jan 24 2013 Kalev Lember <kalevlember@gmail.com> - 6.2.0.873-3
+- Fix the postun script to not use ldconfig as the interpreter
+
+* Wed Jan 23 2013 Chris Leech <cleech@redhat.com> - 6.2.0.873-2
+- package iscsi_mark_root_nodes script, it's being referenced by the unit files
+
+* Tue Jan 22 2013 Chris Leech <cleech@redhat.com> - 6.2.0.873-1
+- rebase to new upstream code
+- systemd conversion
+- 565245 Fix multilib issues caused by timestamp in doxygen footers
+
+* Thu Jul 19 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 6.2.0.872-19
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Tue Feb 14 2012 Mike Christie <mchristi@redhat.com> 6.2.0.872.18
+- 789683 Fix boot slow down when the iscsi service is started
+  (regression added in 6.2.0.872.16 when the nm wait was added).
+
+* Mon Feb 6 2012 Mike Christie <mchristi@redhat.com> 6.2.0.872.17
+- 786174 Change iscsid/iscsi service startup, so it always starts
+  when called.
+
+* Sat Feb 4 2012 Mike Christie <mchristi@redhat.com> 6.2.0.872.16
+- 747479 Fix iscsidevs handling of network requirement
+
+* Fri Jan 13 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 6.2.0.872-15
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Wed Nov 30 2011 Mike Christie <mcrhsit@redhat.com> 6.2.0.872.14
+- Fix version string to reflect fedora and not rhel.
+
+* Tue Oct 18 2011 Mike Christie <mcrhsit@redhat.com> 6.2.0.872.13
+- Update iscsi tools.
+
+* Sat Apr 30 2011 Hans de Goede <hdegoede@redhat.com> - 6.2.0.872-12
+- Change iscsi init scripts to check for networking being actually up, rather
+  then for NetworkManager being started (#692230)
+
+* Tue Apr 26 2011 Hans de Goede <hdegoede@redhat.com> - 6.2.0.872-11
+- Fix iscsid autostarting when upgrading from an older version
+  (add iscsid.startup key to iscsid.conf on upgrade)
+- Fix printing of [ OK ] when successfully stopping iscsid
+- systemd related fixes:
+ - Add Should-Start/Stop tgtd to iscsi init script to fix (re)boot from
+   hanging when using locally hosted targets
+ - %%ghost /var/lock/iscsi and contents (#656605)
+
+* Mon Apr 25 2011 Mike Christie <mchristi@redhat.com> 6.2.0.872-10
+- Fix iscsi init scripts check for networking being up (#692230)
+
+* Wed Feb 09 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 6.2.0.872-9
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild