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/SOURCES/0001-Add-error-message-for-new-ISCSI_ERR_NOP_TIMEDOUT.patch b/SOURCES/0001-Add-error-message-for-new-ISCSI_ERR_NOP_TIMEDOUT.patch new file mode 100644 index 0000000..a5364ec --- /dev/null +++ b/SOURCES/0001-Add-error-message-for-new-ISCSI_ERR_NOP_TIMEDOUT.patch @@ -0,0 +1,27 @@ +From 6b5eb1008e593d7cadbc4165ee6978d8d2ff4f76 Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Thu, 12 Apr 2018 18:17:00 -0700 +Subject: [PATCH 1/1] Add error message for new ISCSI_ERR_NOP_TIMEDOUT + +This new error enum was added with commit 5c654fcc617a, +but it needs to be added to kern_err_table as well. +--- + usr/kern_err_table.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/usr/kern_err_table.c b/usr/kern_err_table.c +index a6ea8fb..8af6f05 100644 +--- a/usr/kern_err_table.c ++++ b/usr/kern_err_table.c +@@ -77,6 +77,8 @@ const char *kern_err_code_to_string(int err) + case ISCSI_ERR_SCSI_EH_SESSION_RST: + return "ISCSI_ERR_SCSI_EH_SESSION_RST: Session was dropped as " + "a result of SCSI error recovery"; ++ case ISCSI_ERR_NOP_TIMEDOUT: ++ return "ISCSI_ERR_NOP_TIMEDOUT: A NOP has timed out"; + default: + return "Invalid or unknown error code"; + } +-- +2.21.0 + diff --git a/SOURCES/0001-Keep-iscsi_if-in-sync-with-kernel-version.patch b/SOURCES/0001-Keep-iscsi_if-in-sync-with-kernel-version.patch new file mode 100644 index 0000000..d9a6d07 --- /dev/null +++ b/SOURCES/0001-Keep-iscsi_if-in-sync-with-kernel-version.patch @@ -0,0 +1,59 @@ +From 5c654fcc617a7b42d93a53660ea578b2ab3efa3e Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Thu, 12 Apr 2018 16:49:00 -0700 +Subject: [PATCH 1/1] Keep iscsi_if in sync with kernel version. + +Add in a new enum for iscsi_param and iscsi_err, +and add a couple of new (higher) port speeds. + +Also added a note about having to keep this +file in sync with the kernel version. +--- + include/iscsi_if.h | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/include/iscsi_if.h b/include/iscsi_if.h +index 5be1981..2d46214 100644 +--- a/include/iscsi_if.h ++++ b/include/iscsi_if.h +@@ -30,6 +30,12 @@ + + #include "iscsi_proto.h" + ++/* ++ * NOTE: This file should be kept in sync with the kernel include file ++ * of the same name. In particular, iscsi_param and iscsi_err need ++ * to be in sync. ++ */ ++ + #define ISCSI_NL_GRP_ISCSID 1 + #define ISCSI_NL_GRP_UIP 2 + +@@ -536,6 +542,7 @@ enum iscsi_err { + ISCSI_ERR_XMIT_FAILED = ISCSI_ERR_BASE + 19, + ISCSI_ERR_TCP_CONN_CLOSE = ISCSI_ERR_BASE + 20, + ISCSI_ERR_SCSI_EH_SESSION_RST = ISCSI_ERR_BASE + 21, ++ ISCSI_ERR_NOP_TIMEDOUT = ISCSI_ERR_BASE + 22, + }; + + /* +@@ -627,6 +634,7 @@ enum iscsi_param { + + ISCSI_PARAM_DISCOVERY_PARENT_IDX, + ISCSI_PARAM_DISCOVERY_PARENT_TYPE, ++ ISCSI_PARAM_LOCAL_IPADDR, + /* must always be last */ + ISCSI_PARAM_MAX, + }; +@@ -731,6 +739,8 @@ enum iscsi_port_speed { + ISCSI_PORT_SPEED_100MBPS = 0x4, + ISCSI_PORT_SPEED_1GBPS = 0x8, + ISCSI_PORT_SPEED_10GBPS = 0x10, ++ ISCSI_PORT_SPEED_25GBPS = 0x20, ++ ISCSI_PORT_SPEED_40GBPS = 0x40, + }; + + /* iSCSI port state */ +-- +2.21.0 + diff --git a/SOURCES/0001-enable-MaxOutstandingR2T-negotiation.patch b/SOURCES/0001-enable-MaxOutstandingR2T-negotiation.patch new file mode 100644 index 0000000..ad99460 --- /dev/null +++ b/SOURCES/0001-enable-MaxOutstandingR2T-negotiation.patch @@ -0,0 +1,91 @@ +From a54966e121ef5deaee2e113d1c912a7dd85aca86 Mon Sep 17 00:00:00 2001 +From: Chris Leech +Date: Wed, 20 Jun 2018 22:09:35 -0700 +Subject: [PATCH 1/1] enable MaxOutstandingR2T negotiation + +The iscsi_tcp kernel side R2T handling is in libiscsi_tcp and is shared +with at least cxgbi3, and the drivers claim CAP_MULTI_R2T. But the +iscsid login parameter negotiation code never enabled MaxOutstandingR2T. + +I've tested this against an LIO target, negotiating to 2 and 4 R2Ts. +The network traces looked good, with the initiator keeping track of +multiple outstanding R2Ts and using the correct next identifier. +Throughput of bulk write data was improved, even over low latency virtio +links. + +Signed-off-by: Chris Leech +--- + usr/initiator.h | 1 + + usr/initiator_common.c | 3 ++- + usr/login.c | 14 +++++--------- + 3 files changed, 8 insertions(+), 10 deletions(-) + +diff --git a/usr/initiator.h b/usr/initiator.h +index 4f96d6b6e2c5..3ee1454b6de2 100644 +--- a/usr/initiator.h ++++ b/usr/initiator.h +@@ -212,6 +212,7 @@ typedef struct iscsi_session { + int erl; + uint32_t imm_data_en; + uint32_t initial_r2t_en; ++ uint32_t max_r2t; + uint32_t fast_abort; + uint32_t first_burst; + uint32_t max_burst; +diff --git a/usr/initiator_common.c b/usr/initiator_common.c +index 191e779bb942..d00bd9e7469b 100644 +--- a/usr/initiator_common.c ++++ b/usr/initiator_common.c +@@ -173,6 +173,7 @@ iscsi_copy_operational_params(struct iscsi_conn *conn, + + /* session's operational parameters */ + session->initial_r2t_en = session_conf->InitialR2T; ++ session->max_r2t = session_conf->MaxOutstandingR2T; + session->imm_data_en = session_conf->ImmediateData; + session->first_burst = align_32_down(session_conf->FirstBurstLength); + /* +@@ -375,7 +376,7 @@ int iscsi_session_set_neg_params(struct iscsi_conn *conn) + .conn_only = 0, + }, { + .param = ISCSI_PARAM_MAX_R2T, +- .value = &one, /* FIXME: session->max_r2t */ ++ .value = &session->max_r2t, + .type = ISCSI_INT, + .conn_only = 0, + }, { +diff --git a/usr/login.c b/usr/login.c +index 294ad0bf73fd..d7dad21180cf 100644 +--- a/usr/login.c ++++ b/usr/login.c +@@ -524,14 +524,9 @@ get_op_params_text_keys(iscsi_session_t *session, int cid, + text = value_end; + } else if (iscsi_find_key_value("MaxOutstandingR2T", text, end, &value, + &value_end)) { +- if (session->type == ISCSI_SESSION_TYPE_NORMAL) { +- if (strcmp(value, "1")) { +- log_error("Login negotiation " +- "failed, can't accept Max" +- "OutstandingR2T %s", value); +- return LOGIN_NEGOTIATION_FAILED; +- } +- } else ++ if (session->type == ISCSI_SESSION_TYPE_NORMAL) ++ session->max_r2t = strtoul(value, NULL, 0); ++ else + session->irrelevant_keys_bitmap |= + IRRELEVANT_MAXOUTSTANDINGR2T; + text = value_end; +@@ -810,8 +805,9 @@ add_params_normal_session(iscsi_session_t *session, struct iscsi_hdr *pdu, + return 0; + + /* these we must have */ ++ sprintf(value, "%d", session->max_r2t); + if (!iscsi_add_text(pdu, data, max_data_length, +- "MaxOutstandingR2T", "1")) ++ "MaxOutstandingR2T", value)) + return 0; + if (!iscsi_add_text(pdu, data, max_data_length, + "MaxConnections", "1")) +-- +2.14.4 + diff --git a/SOURCES/0001-iscsid-Update-boot-gateway-information-during-sync_s.patch b/SOURCES/0001-iscsid-Update-boot-gateway-information-during-sync_s.patch new file mode 100644 index 0000000..10e22ca --- /dev/null +++ b/SOURCES/0001-iscsid-Update-boot-gateway-information-during-sync_s.patch @@ -0,0 +1,33 @@ +From d295d0745829f9eee1f4f0e1e3328f9bb4f2dc16 Mon Sep 17 00:00:00 2001 +From: Manish Rangankar +Date: Wed, 20 Jun 2018 16:19:24 +0530 +Subject: [PATCH 1/1] iscsid: Update boot gateway information during + sync_session. + +During boot for SAN scenario after switch root iscsid not +passing gateway information to iscsiuio. + +Signed-off-by: Manish Rangankar +--- + usr/iscsi_sysfs.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/usr/iscsi_sysfs.c b/usr/iscsi_sysfs.c +index fdc26c7..8e89b30 100644 +--- a/usr/iscsi_sysfs.c ++++ b/usr/iscsi_sysfs.c +@@ -706,6 +706,11 @@ static int iscsi_sysfs_read_boot(struct iface_rec *iface, char *session) + log_debug(5, "could not read %s/%s/subnet", boot_root, + boot_nic); + ++ if (sysfs_get_str(boot_nic, boot_root, "gateway", ++ iface->gateway, NI_MAXHOST)) ++ log_debug(5, "could not read %s/%s/gateway", boot_root, ++ boot_nic); ++ + log_debug(5, "sysfs read boot returns %s/%s/ vlan = %d subnet = %s", + boot_root, boot_nic, iface->vlan_id, iface->subnet_mask); + return 0; +-- +2.21.0 + diff --git a/SOURCES/0001-iscsiuio-allow-processing-of-iscsid-requests-in-DHCP.patch b/SOURCES/0001-iscsiuio-allow-processing-of-iscsid-requests-in-DHCP.patch new file mode 100644 index 0000000..ca6319c --- /dev/null +++ b/SOURCES/0001-iscsiuio-allow-processing-of-iscsid-requests-in-DHCP.patch @@ -0,0 +1,47 @@ +From 6997f36c44cd7046d1be1248fd864d9a068faff5 Mon Sep 17 00:00:00 2001 +From: Nilesh Javali +Date: Tue, 25 Jun 2019 01:05:01 -0400 +Subject: [PATCH 1/1] iscsiuio: allow processing of iscsid requests in DHCP + failure condition + +In the event of DHCP failure, killing of enable_nic_thread did not +process any iscsid requests leading to error, +iscsistart: Could not broadcast to uIP after 5 tries +and login failure to next active path. +Do not kill enable_nic_thread and allow further processing of iscsid +requests and performing login to next active path. + +Signed-off-by: Nilesh Javali +--- + iscsiuio/src/unix/nic.c | 15 --------------- + 1 file changed, 15 deletions(-) + +diff --git a/iscsiuio/src/unix/nic.c b/iscsiuio/src/unix/nic.c +index bd415c2..f449935 100644 +--- a/iscsiuio/src/unix/nic.c ++++ b/iscsiuio/src/unix/nic.c +@@ -1318,21 +1318,6 @@ static int do_acquisition(nic_t *nic, nic_interface_t *nic_iface, + /* For DHCPv4 failure, the ustack must be cleaned so + it can re-acquire on the next iscsid request */ + uip_reset(&nic_iface->ustack); +- +- /* Signal that the device enable is +- done */ +- pthread_cond_broadcast(&nic->enable_done_cond); +- pthread_mutex_unlock(&nic->nic_mutex); +- +- if (nic->enable_thread == INVALID_THREAD) +- goto dhcp_err; +- +- rc = pthread_cancel(nic->enable_thread); +- if (rc != 0) +- LOG_ERR(PFX "%s: Couldn't cancel " +- "enable nic thread", nic->log_name); +-dhcp_err: +- pthread_mutex_lock(&nic->nic_mutex); + goto error; + } + +-- +2.21.0 + diff --git a/SOURCES/0001-iscsiuio-update-version-to-0.7.8.6.patch b/SOURCES/0001-iscsiuio-update-version-to-0.7.8.6.patch new file mode 100644 index 0000000..c57f09f --- /dev/null +++ b/SOURCES/0001-iscsiuio-update-version-to-0.7.8.6.patch @@ -0,0 +1,80 @@ +From fb7e87dbe5bad120c88f7e430dfdc627d247fa4c Mon Sep 17 00:00:00 2001 +From: Nilesh Javali +Date: Thu, 27 Jun 2019 05:39:43 -0400 +Subject: [PATCH 1/1] iscsiuio: update version to 0.7.8.6 + +Signed-off-by: Nilesh Javali +--- + iscsiuio/README | 4 ++-- + iscsiuio/RELEASE.TXT | 19 +++++++++++++++++-- + iscsiuio/configure.ac | 4 ++-- + 3 files changed, 21 insertions(+), 6 deletions(-) + +diff --git a/iscsiuio/README b/iscsiuio/README +index 1915005..53b700c 100644 +--- a/iscsiuio/README ++++ b/iscsiuio/README +@@ -1,6 +1,6 @@ + Iscsiuio Userspace Tool +-Version 0.7.8.5 +-Nov 20, 2018 ++Version 0.7.8.6 ++Jun 27, 2019 + ------------------------------------------------------ + + 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 0ebf27f..28e681b 100644 +--- a/iscsiuio/RELEASE.TXT ++++ b/iscsiuio/RELEASE.TXT +@@ -1,7 +1,7 @@ + Release Notes + QLogic uIP Linux Driver +- Version 0.7.8.5 +- 11/20/2018 ++ Version 0.7.8.6 ++ 06/27/2019 + + QLogic Corporation + 26650 Aliso Viejo Pkwy, +@@ -11,6 +11,21 @@ + Copyright (c) 2014, QLogic Corporation + All rights reserved + ++uIP v0.7.8.6 (Jun 27, 2019) ++======================================================= ++ Fixes: ++ ------- ++ 1. Problem: OS fails to boot after one path is ++ disconnected from iSCSI MPIO config. ++ Change: In the event of DHCP failure, killing of enable_nic_thread did ++ not process any iscsid requests leading to error, ++ iscsistart: Could not broadcast to uIP after 5 tries ++ and login failure of active path. ++ Added fix to not kill enable_nic_thread and allow further ++ processing of iscsid requests and performing login ++ to next active path. ++ Impact: All ++ + uIP v0.7.8.5 (Nov 20, 2018) + ======================================================= + Fixes: +diff --git a/iscsiuio/configure.ac b/iscsiuio/configure.ac +index ae23078..7d53323 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.5 ++VERSION=0.7.8.6 + +-AC_INIT([iscsiuio], [0.7.8.5], [QLogic-Storage-Upstream@cavium.com]) ++AC_INIT([iscsiuio], [0.7.8.6], [QLogic-Storage-Upstream@cavium.com]) + + AM_INIT_AUTOMAKE + AC_CONFIG_HEADER(config.h) +-- +2.21.0 + diff --git a/SOURCES/0001-rec-update-disable-the-idbm_lock-in-read-write-when-.patch b/SOURCES/0001-rec-update-disable-the-idbm_lock-in-read-write-when-.patch new file mode 100644 index 0000000..5fa66f5 --- /dev/null +++ b/SOURCES/0001-rec-update-disable-the-idbm_lock-in-read-write-when-.patch @@ -0,0 +1,517 @@ +From c64a92e32187d24dda44d983809f36e20d9cb92f Mon Sep 17 00:00:00 2001 +From: Xiubo Li +Date: Tue, 18 Jun 2019 10:12:31 +0800 +Subject: [PATCH 1/1] rec update: disable the idbm_lock in read/write when + updating the rec + +The iscsiadm is getting a file lock while writing out records, and +updates are a read-modify-write operation and currently only the +write is locked. + +So the parallel requests are reading in the pre-update record and +then overwriting each other with the writes. + +Fixing RHBZ#1624670 + +Signed-off-by: Xiubo Li +--- + usr/idbm.c | 132 +++++++++++++++++++++++++++++++------------------ + usr/idbm.h | 17 ++++--- + usr/iface.c | 2 +- + usr/iscsiadm.c | 18 +++---- + usr/iscsid.c | 2 +- + 5 files changed, 105 insertions(+), 66 deletions(-) + +diff --git a/usr/idbm.c b/usr/idbm.c +index c238cc7..ca2557f 100644 +--- a/usr/idbm.c ++++ b/usr/idbm.c +@@ -1401,7 +1401,12 @@ static FILE *idbm_open_rec_r(char *portal, char *config) + return fopen(portal, "r"); + } + +-static int __idbm_rec_read(node_rec_t *out_rec, char *conf) ++/* ++ * When the disable_lock param is true, the idbm_lock/idbm_unlock needs ++ * to be holt by the caller, this will avoid overwriting each other in ++ * case of updating(read-modify-write) the recs in parallel. ++ */ ++static int __idbm_rec_read(node_rec_t *out_rec, char *conf, bool disable_lock) + { + recinfo_t *info; + FILE *f; +@@ -1411,9 +1416,11 @@ static int __idbm_rec_read(node_rec_t *out_rec, char *conf) + if (!info) + return ISCSI_ERR_NOMEM; + +- rc = idbm_lock(); +- if (rc) +- goto free_info; ++ if (!disable_lock) { ++ rc = idbm_lock(); ++ if (rc) ++ goto free_info; ++ } + + f = fopen(conf, "r"); + if (!f) { +@@ -1430,15 +1437,21 @@ static int __idbm_rec_read(node_rec_t *out_rec, char *conf) + fclose(f); + + unlock: +- idbm_unlock(); ++ if (!disable_lock) ++ idbm_unlock(); + free_info: + free(info); + return rc; + } + ++/* ++ * When the disable_lock param is true, the idbm_lock/idbm_unlock needs ++ * to be holt by the caller, this will avoid overwriting each other in ++ * case of updating(read-modify-write) the recs in parallel. ++ */ + int + idbm_rec_read(node_rec_t *out_rec, char *targetname, int tpgt, +- char *ip, int port, struct iface_rec *iface) ++ char *ip, int port, struct iface_rec *iface, bool disable_lock) + { + struct stat statb; + char *portal; +@@ -1470,7 +1483,7 @@ idbm_rec_read(node_rec_t *out_rec, char *targetname, int tpgt, + } + + read: +- rc = __idbm_rec_read(out_rec, portal); ++ rc = __idbm_rec_read(out_rec, portal, disable_lock); + free_portal: + free(portal); + return rc; +@@ -1526,7 +1539,7 @@ static int idbm_print_discovered(discovery_rec_t *drec, int info_level) + int num_found = 0; + + if (info_level < 1) +- idbm_for_each_rec(&num_found, drec, print_discovered_flat); ++ idbm_for_each_rec(&num_found, drec, print_discovered_flat, false); + else { + struct discovered_tree_info tree_info; + struct node_rec last_rec; +@@ -1536,7 +1549,7 @@ static int idbm_print_discovered(discovery_rec_t *drec, int info_level) + tree_info.drec = drec; + tree_info.last_rec = &last_rec; + +- idbm_for_each_rec(&num_found, &tree_info, print_discovered_tree); ++ idbm_for_each_rec(&num_found, &tree_info, print_discovered_tree, false); + } + return num_found; + } +@@ -1708,9 +1721,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) ++static int idbm_for_each_iface(int *found, void *data, idbm_iface_op_fn *fn, ++ char *targetname, int tpgt, char *ip, int port, ++ bool ruw_lock) + { + DIR *iface_dirfd; + struct dirent *iface_dent; +@@ -1735,7 +1748,7 @@ static int idbm_for_each_iface(int *found, void *data, + goto free_portal; + } + +- rc = __idbm_rec_read(&rec, portal); ++ rc = __idbm_rec_read(&rec, portal, ruw_lock); + if (rc) + goto free_portal; + +@@ -1768,7 +1781,7 @@ read_iface: + memset(portal, 0, PATH_MAX); + snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d/%s", NODE_CONFIG_DIR, + targetname, ip, port, tpgt, iface_dent->d_name); +- if (__idbm_rec_read(&rec, portal)) ++ if (__idbm_rec_read(&rec, portal, ruw_lock)) + continue; + + curr_rc = fn(data, &rec); +@@ -1790,7 +1803,7 @@ free_portal: + * The portal could be a file or dir with interfaces + */ + int idbm_for_each_portal(int *found, void *data, idbm_portal_op_fn *fn, +- char *targetname) ++ char *targetname, bool ruw_lock) + { + DIR *portal_dirfd; + struct dirent *portal_dent; +@@ -1827,7 +1840,8 @@ int idbm_for_each_portal(int *found, void *data, idbm_portal_op_fn *fn, + + curr_rc = fn(found, data, targetname, + tmp_tpgt ? atoi(tmp_tpgt) : -1, +- portal_dent->d_name, atoi(tmp_port)); ++ portal_dent->d_name, atoi(tmp_port), ++ ruw_lock); + /* less than zero means it was not a match */ + if (curr_rc > 0 && !rc) + rc = curr_rc; +@@ -1838,7 +1852,7 @@ done: + return rc; + } + +-int idbm_for_each_node(int *found, void *data, idbm_node_op_fn *fn) ++int idbm_for_each_node(int *found, void *data, idbm_node_op_fn *fn, bool ruw_lock) + { + DIR *node_dirfd; + struct dirent *node_dent; +@@ -1859,7 +1873,7 @@ int idbm_for_each_node(int *found, void *data, idbm_node_op_fn *fn) + continue; + + log_debug(5, "searching %s", node_dent->d_name); +- curr_rc = fn(found, data, node_dent->d_name); ++ curr_rc = fn(found, data, node_dent->d_name, ruw_lock); + /* less than zero means it was not a match */ + if (curr_rc > 0 && !rc) + rc = curr_rc; +@@ -1877,18 +1891,30 @@ static int iface_fn(void *data, node_rec_t *rec) + } + + static int portal_fn(int *found, void *data, char *targetname, +- int tpgt, char *ip, int port) ++ int tpgt, char *ip, int port, bool ruw_lock) + { +- return idbm_for_each_iface(found, data, iface_fn, targetname, +- tpgt, ip, port); ++ int rc; ++ ++ if (ruw_lock) { ++ rc = idbm_lock(); ++ if (rc) ++ return rc; ++ } ++ ++ rc = idbm_for_each_iface(found, data, iface_fn, targetname, ++ tpgt, ip, port, ruw_lock); ++ if (ruw_lock) ++ idbm_unlock(); ++ ++ return rc; + } + +-static int node_fn(int *found, void *data, char *targetname) ++static int node_fn(int *found, void *data, char *targetname, bool ruw_lock) + { +- return idbm_for_each_portal(found, data, portal_fn, targetname); ++ return idbm_for_each_portal(found, data, portal_fn, targetname, ruw_lock); + } + +-int idbm_for_each_rec(int *found, void *data, idbm_iface_op_fn *fn) ++int idbm_for_each_rec(int *found, void *data, idbm_iface_op_fn *fn, bool ruw_lock) + { + struct rec_op_data op_data; + +@@ -1896,7 +1922,7 @@ int idbm_for_each_rec(int *found, void *data, idbm_iface_op_fn *fn) + op_data.data = data; + op_data.fn = fn; + +- return idbm_for_each_node(found, &op_data, node_fn); ++ return idbm_for_each_node(found, &op_data, node_fn, ruw_lock); + } + + static struct { +@@ -2141,7 +2167,12 @@ free_portal: + return rc; + } + +-static int idbm_rec_write(node_rec_t *rec) ++/* ++ * When the disable_lock param is true, the idbm_lock/idbm_unlock needs ++ * to be holt by the caller, this will avoid overwriting each other in ++ * case of updating(read-modify-write) the recs in parallel. ++ */ ++static int idbm_rec_write(node_rec_t *rec, bool disable_lock) + { + char *portal; + int rc = 0; +@@ -2172,9 +2203,11 @@ static int idbm_rec_write(node_rec_t *rec) + } + } + +- rc = idbm_lock(); +- if (rc) +- goto free_portal; ++ if (!disable_lock) { ++ rc = idbm_lock(); ++ if (rc) ++ goto free_portal; ++ } + + if (rec->tpgt == PORTAL_GROUP_TAG_UNKNOWN) + /* old style portal as config */ +@@ -2182,7 +2215,8 @@ static int idbm_rec_write(node_rec_t *rec) + else + rc = idbm_rec_write_new(rec); + +- idbm_unlock(); ++ if (!disable_lock) ++ idbm_unlock(); + free_portal: + free(portal); + return rc; +@@ -2357,18 +2391,24 @@ static int setup_disc_to_node_link(char *disc_portal, node_rec_t *rec) + int idbm_add_node(node_rec_t *newrec, discovery_rec_t *drec, int overwrite) + { + node_rec_t rec; +- char *node_portal, *disc_portal; ++ char *node_portal = NULL, *disc_portal; + int rc; + ++ rc = idbm_lock(); ++ if (rc) ++ return rc; ++ + if (!idbm_rec_read(&rec, newrec->name, newrec->tpgt, + newrec->conn[0].address, newrec->conn[0].port, +- &newrec->iface)) { +- if (!overwrite) +- return 0; ++ &newrec->iface, true)) { ++ if (!overwrite) { ++ rc = 0; ++ goto unlock; ++ } + + rc = idbm_delete_node(&rec); + if (rc) +- return rc; ++ goto unlock; + log_debug(7, "overwriting existing record"); + } else + log_debug(7, "adding new DB record"); +@@ -2379,17 +2419,19 @@ int idbm_add_node(node_rec_t *newrec, discovery_rec_t *drec, int overwrite) + strcpy(newrec->disc_address, drec->address); + } + +- rc = idbm_rec_write(newrec); ++ rc = idbm_rec_write(newrec, true); + /* + * if a old app passed in a bogus tpgt then we do not create links + * since it will set a different tpgt in another iscsiadm call + */ + if (rc || !drec || newrec->tpgt == PORTAL_GROUP_TAG_UNKNOWN) +- return rc; ++ goto unlock; + + node_portal = calloc(2, PATH_MAX); +- if (!node_portal) +- return ISCSI_ERR_NOMEM; ++ if (!node_portal) { ++ rc = ISCSI_ERR_NOMEM; ++ goto unlock; ++ } + + disc_portal = node_portal + PATH_MAX; + snprintf(node_portal, PATH_MAX, "%s/%s/%s,%d,%d", NODE_CONFIG_DIR, +@@ -2397,15 +2439,11 @@ int idbm_add_node(node_rec_t *newrec, discovery_rec_t *drec, int overwrite) + newrec->tpgt); + rc = setup_disc_to_node_link(disc_portal, newrec); + if (rc) +- goto free_portal; ++ goto unlock; + + log_debug(7, "node addition making link from %s to %s", node_portal, + disc_portal); + +- rc = idbm_lock(); +- if (rc) +- goto free_portal; +- + if (symlink(node_portal, disc_portal)) { + if (errno == EEXIST) + log_debug(7, "link from %s to %s exists", node_portal, +@@ -2417,8 +2455,8 @@ int idbm_add_node(node_rec_t *newrec, discovery_rec_t *drec, int overwrite) + strerror(errno)); + } + } ++unlock: + idbm_unlock(); +-free_portal: + free(node_portal); + return rc; + } +@@ -2657,7 +2695,7 @@ static int idbm_remove_disc_to_node_link(node_rec_t *rec, + rec->name, rec->conn[0].address, rec->conn[0].port, rec->tpgt, + rec->iface.name); + +- rc = __idbm_rec_read(tmprec, portal); ++ rc = __idbm_rec_read(tmprec, portal, false); + if (rc) { + /* old style recs will not have tpgt or a link so skip */ + rc = 0; +@@ -2884,7 +2922,7 @@ int idbm_node_set_param(void *data, node_rec_t *rec) + if (rc) + return rc; + +- return idbm_rec_write(rec); ++ return idbm_rec_write(rec, true); + } + + int idbm_discovery_set_param(void *data, discovery_rec_t *rec) +diff --git a/usr/idbm.h b/usr/idbm.h +index 411dd82..8571a8c 100644 +--- a/usr/idbm.h ++++ b/usr/idbm.h +@@ -23,6 +23,7 @@ + #define IDBM_H + + #include ++#include + #include + #include "initiator.h" + #include "config.h" +@@ -91,22 +92,22 @@ struct user_param { + }; + + typedef int (idbm_iface_op_fn)(void *data, node_rec_t *rec); +-typedef int (idbm_portal_op_fn)(int *found, void *data, +- char *targetname, int tpgt, char *ip, int port); ++typedef int (idbm_portal_op_fn)(int *found, void *data, char *targetname, ++ int tpgt, char *ip, int port, bool ruw_lock); + typedef int (idbm_node_op_fn)(int *found, void *data, +- char *targetname); ++ char *targetname, bool ruw_lock); + + struct rec_op_data { + void *data; + node_rec_t *match_rec; + idbm_iface_op_fn *fn; + }; +-extern int idbm_for_each_portal(int *found, void *data, +- idbm_portal_op_fn *fn, char *targetname); ++extern int idbm_for_each_portal(int *found, void *data, idbm_portal_op_fn *fn, ++ char *targetname, bool ruw_lock); + extern int idbm_for_each_node(int *found, void *data, +- idbm_node_op_fn *fn); ++ idbm_node_op_fn *fn, bool ruw_lock); + extern int idbm_for_each_rec(int *found, void *data, +- idbm_iface_op_fn *fn); ++ idbm_iface_op_fn *fn, bool ruw_lock); + + + typedef int (idbm_drec_op_fn)(void *data, discovery_rec_t *drec); +@@ -145,7 +146,7 @@ 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, + int tpgt, char *addr, int port, +- struct iface_rec *iface); ++ struct iface_rec *iface, bool disable_lock); + extern int idbm_node_set_rec_from_param(struct list_head *params, + node_rec_t *rec, int verify); + extern int idbm_node_set_param(void *data, node_rec_t *rec); +diff --git a/usr/iface.c b/usr/iface.c +index 74e63f6..b2e6db6 100644 +--- a/usr/iface.c ++++ b/usr/iface.c +@@ -855,7 +855,7 @@ int iface_print_tree(void *data, struct iface_rec *iface) + print_data.match_iface = iface; + print_data.last_rec = &last_rec; + +- idbm_for_each_rec(&num_found, &print_data, iface_print_nodes); ++ idbm_for_each_rec(&num_found, &print_data, iface_print_nodes, false); + return 0; + } + +diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c +index 6245e89..c2b047e 100644 +--- a/usr/iscsiadm.c ++++ b/usr/iscsiadm.c +@@ -398,7 +398,7 @@ __logout_by_startup(void *data, struct list_head *list, + memset(&rec, 0, sizeof(node_rec_t)); + if (idbm_rec_read(&rec, info->targetname, info->tpgt, + info->persistent_address, +- info->persistent_port, &info->iface)) { ++ info->persistent_port, &info->iface, false)) { + /* + * this is due to a HW driver or some other driver + * not hooked in +@@ -515,7 +515,7 @@ login_by_startup(char *mode) + startup.mode = mode; + INIT_LIST_HEAD(&startup.all_logins); + INIT_LIST_HEAD(&startup.leading_logins); +- err = idbm_for_each_rec(&nr_found, &startup, link_startup_recs); ++ err = idbm_for_each_rec(&nr_found, &startup, link_startup_recs, false); + if (err && (!list_empty(&startup.all_logins) || + !list_empty(&startup.leading_logins))) + /* log msg and try to log into what we found */ +@@ -662,7 +662,7 @@ static int __for_each_matched_rec(int verbose, struct node_rec *rec, + op_data.match_rec = rec; + op_data.fn = fn; + +- rc = idbm_for_each_rec(&nr_found, &op_data, rec_match_fn); ++ rc = idbm_for_each_rec(&nr_found, &op_data, rec_match_fn, true); + if (rc) { + if (verbose) + log_error("Could not execute operation on all " +@@ -915,8 +915,8 @@ done: + return rc; + } + +-static int add_static_portal(int *found, void *data, +- char *targetname, int tpgt, char *ip, int port) ++static int add_static_portal(int *found, void *data, char *targetname, ++ int tpgt, char *ip, int port, bool ruw_lock) + { + node_rec_t *rec = data; + +@@ -932,7 +932,7 @@ static int add_static_portal(int *found, void *data, + } + + static int add_static_node(int *found, void *data, +- char *targetname) ++ char *targetname, bool ruw_lock) + { + node_rec_t *rec = data; + +@@ -950,14 +950,14 @@ static int add_static_node(int *found, void *data, + rec->conn[0].port, &rec->iface); + search: + return idbm_for_each_portal(found, data, add_static_portal, +- targetname); ++ targetname, false); + } + + static int add_static_recs(struct node_rec *rec) + { + int rc, nr_found = 0; + +- rc = idbm_for_each_node(&nr_found, rec, add_static_node); ++ rc = idbm_for_each_node(&nr_found, rec, add_static_node, false); + if (rc) + goto done; + /* success */ +@@ -1048,7 +1048,7 @@ exec_disc_op_on_recs(discovery_rec_t *drec, struct list_head *rec_list, + + /* clean up node db */ + if (op & OP_DELETE) +- idbm_for_each_rec(&found, rec_list, delete_stale_rec); ++ idbm_for_each_rec(&found, rec_list, delete_stale_rec, false); + + if (op & OP_NEW || op & OP_UPDATE) { + /* now add/update records */ +diff --git a/usr/iscsid.c b/usr/iscsid.c +index f0017e5..01b90c9 100644 +--- a/usr/iscsid.c ++++ b/usr/iscsid.c +@@ -233,7 +233,7 @@ static int sync_session(void *data, struct session_info *info) + + if (idbm_rec_read(&rec, info->targetname, info->tpgt, + info->persistent_address, info->persistent_port, +- &info->iface)) { ++ &info->iface, false)) { + log_warning("Could not read data from db. Using default and " + "currently negotiated values"); + setup_rec_from_negotiated_values(&rec, info); +-- +2.21.0 + 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 +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 +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 +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 + #include + #include ++#include + #include + #include + +@@ -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 +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 +--- + 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 +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 +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 +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,,, + qla4xxx.00:0e:1e:04:8b:2e qla4xxx,00:0e:1e:04:8b:2e,,, + + +-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 +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 +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..1f66b96 --- /dev/null +++ b/SOURCES/0154-add-libiscsi.patch @@ -0,0 +1,3872 @@ +unchanged: +--- 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 +unchanged: +--- /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 +unchanged: +--- /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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#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(¶ms); ++ param = idbm_alloc_user_param(parameter, value); ++ if (!param) { ++ rc = ENOMEM; ++ goto leave; ++ } ++ list_add_tail(¶ms, ¶m->list); ++ ++ CHECK(idbm_for_each_iface(&nr_found, ¶ms, 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; ++} +unchanged: +--- /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 , where is the value of ++# the FILE_VERSION_FILTER tag, and 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 , where ++# is the value of the INPUT_FILTER tag, and 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 ++# Qt Help Project / Namespace. ++ ++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 ++# Qt Help Project / Virtual Folders. ++ ++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 +unchanged: +--- /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 ++ * 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 ++ ++#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 +unchanged: +--- /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 ++ * 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 ++#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, ++ ¶meter, &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, ¶meter)) ++ 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); ++} +unchanged: +--- /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]) +unchanged: +--- /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 ++ * 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 ++#include ++#include ++#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; ++} +unchanged: +--- /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 ++ * 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 ++#include ++#include ++#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; ++} +unchanged: +--- /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 ++ * 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 ++#include ++#include ++#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; ++} +unchanged: +--- /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 ++ * 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 ++#include ++#include ++#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; ++} +unchanged: +--- /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 ++ * 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 ++#include ++#include ++#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; ++} +unchanged: +--- /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 ++ * 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 ++#include ++#include ++#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; ++} +unchanged: +--- /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 ++ * 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 ++#include ++#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; ++} +unchanged: +--- /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 ++ * 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 ++#include ++#include ++#include ++#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; ++} +unchanged: +--- /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 ++ * 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 ++#include ++#include ++#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; ++} +unchanged: +--- 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 +unchanged: +--- 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 + #include + #include ++#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) +unchanged: +--- a/usr/idbm.c ++++ b/usr/idbm.c +@@ -1725,7 +1725,7 @@ 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, ++int idbm_for_each_iface(int *found, void *data, idbm_iface_op_fn *fn, + char *targetname, int tpgt, char *ip, int port, + bool ruw_lock) + { +unchanged: +--- a/usr/idbm.h ++++ b/usr/idbm.h +@@ -103,6 +103,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, bool ruw_lock); + extern int idbm_for_each_portal(int *found, void *data, idbm_portal_op_fn *fn, + char *targetname, bool ruw_lock); + extern int idbm_for_each_node(int *found, void *data, +unchanged: +--- 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 */ +only in patch2: +unchanged: +--- a/libiscsi/libiscsi.c ++++ b/libiscsi/libiscsi.c +@@ -425,7 +425,7 @@ int libiscsi_node_login(struct libiscsi_context *context, + + CHECK(idbm_for_each_iface(&nr_found, (void*)node->iface, login_helper, + (char *)node->name, node->tpgt, +- (char *)node->address, node->port)) ++ (char *)node->address, node->port, false)) + if (nr_found == 0) { + strcpy(context->error_str, "No such node"); + rc = ENODEV; +@@ -487,7 +487,7 @@ int libiscsi_node_set_parameter(struct libiscsi_context *context, + + CHECK(idbm_for_each_iface(&nr_found, ¶ms, idbm_node_set_param, + (char *)node->name, node->tpgt, +- (char *)node->address, node->port)) ++ (char *)node->address, node->port, false)) + if (nr_found == 0) { + strcpy(context->error_str, "No such node"); + rc = ENODEV; +@@ -549,7 +549,7 @@ int libiscsi_node_get_parameter(struct libiscsi_context *context, + 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)) ++ (char *)node->address, node->port, false)) + if (nr_found == 0) { + strcpy(context->error_str, "No such node"); + rc = ENODEV; 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 +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 +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 +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 +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 @@ ++
++Generated for $projectname by doxygen ++$doxygenversion
++ ++ +-- +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 +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 +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 +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 +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 +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 +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 +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 +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/0184-set-iscsid.safe_logout-to-Yes-by-default.patch b/SOURCES/0184-set-iscsid.safe_logout-to-Yes-by-default.patch new file mode 100644 index 0000000..6226bbb --- /dev/null +++ b/SOURCES/0184-set-iscsid.safe_logout-to-Yes-by-default.patch @@ -0,0 +1,25 @@ +From 486110e2564cd9b677c485ec87b5df1bcb5750f2 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Wed, 29 Aug 2018 16:20:27 -0700 +Subject: [PATCH 184/184] set iscsid.safe_logout to Yes by default + +--- + etc/iscsid.conf | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/etc/iscsid.conf b/etc/iscsid.conf +index cbf2c05aff59..1af8ed2732de 100644 +--- a/etc/iscsid.conf ++++ b/etc/iscsid.conf +@@ -25,7 +25,7 @@ iscsid.startup = /bin/systemctl start iscsid.socket iscsiuio.socket + + # Check for active mounts on devices reachable through a session + # and refuse to logout if there are any. Defaults to "No". +-# iscsid.safe_logout = Yes ++iscsid.safe_logout = Yes + + ############################# + # NIC/HBA and driver settings +-- +2.14.4 + 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..98d4e7a --- /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 +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-14" + #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/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 +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 +-#include +-#include ++#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 +-#include +-#include +-#include + + #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 ++#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. ++ ++ ++ Copyright (C) ++ ++ 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. ++ ++ , 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 . ++# Please send patches to . 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 ." ++ ++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 /* 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 ++ ++ 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 ++ #include ++ ++ 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 ++ 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 ++ #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' /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 ++ echo i586-unisys-sysv4 ++ exit ;; ++ *:UNIX_System_V:4*:FTX*) ++ # From Gerald Hewes . ++ # 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 < ++# include ++#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 ++ 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 ++# 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 < 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 . 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 ." ++ ++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 ++ */ ++ ++#include ++#include ++#include ++#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, ""); ++} ++ ++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 ++ */ ++ ++#ifndef ISNS_ATTRS_H ++#define ISNS_ATTRS_H ++ ++#include ++#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 ++ */ ++ ++#include ++#include ++#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 ++ */ ++ ++#include ++#include ++#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(""); ++ 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(""); ++ 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* 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 ++ */ ++ ++#ifndef BUFFER_H ++#define BUFFER_H ++ ++#include ++#include ++#include ++ ++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 ++ */ ++ ++#include ++#include ++#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(¬ifiers, 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(¬ifiers, ¬->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 ""; ++ ++ 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#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 ++#include ++#include ++#include ++#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 j) { ++ tmp=argv[--i]; ++ for(k=i; k+1 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 j) { ++ tmp=argv[--i]; ++ for(k=i; k+1= 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 ++ */ ++#include ++#include ++#include ++#include ++ ++#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 header file. */ ++#undef HAVE_ERRNO_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_FCNTL_H ++ ++/* Define if you have the 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 header file. */ ++#undef HAVE_INTTYPES_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LOCALE_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_MALLOC_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_MEMORY_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_OPENSSL_CRYPTO_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_SLP_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_STDINT_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_STDLIB_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_STRINGS_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_STRING_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_SYS_STAT_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_SYS_TIME_H ++ ++/* Define to 1 if you have the header file. */ ++#undef HAVE_SYS_TYPES_H ++ ++/* Define to 1 if you have that is POSIX.1 compatible. */ ++#undef HAVE_SYS_WAIT_H ++ ++/* Define to 1 if you have the 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 &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 ++#ifdef HAVE_SYS_TYPES_H ++# include ++#endif ++#ifdef HAVE_SYS_STAT_H ++# include ++#endif ++#ifdef STDC_HEADERS ++# include ++# include ++#else ++# ifdef HAVE_STDLIB_H ++# include ++# endif ++#endif ++#ifdef HAVE_STRING_H ++# if !defined STDC_HEADERS && defined HAVE_MEMORY_H ++# include ++# endif ++# include ++#endif ++#ifdef HAVE_STRINGS_H ++# include ++#endif ++#ifdef HAVE_INTTYPES_H ++# include ++#endif ++#ifdef HAVE_STDINT_H ++# include ++#endif ++#ifdef HAVE_UNISTD_H ++# include ++#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 if you have libraries in a ++ nonstandard directory ++ LIBS libraries to pass to the linker, e.g. -l ++ CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if ++ you have headers in a nonstandard directory ++ 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 ++#include ++#include ++#include ++/* 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 to if __STDC__ is defined, since ++ # 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 ++#else ++# include ++#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 ++_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 to if __STDC__ is defined, since ++ # 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 ++#else ++# include ++#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 ++_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 ++#include ++#include ++#include ++ ++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 ++ ++_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 ++ ++_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 ++#include ++#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 ++ #include ++ ++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 ++ #include ++ ++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 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 ++ ++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 ++ ++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 to if __STDC__ is defined, since ++ # 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 ++#else ++# include ++#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 ++_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 to if __STDC__ is defined, since ++ # 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 ++#else ++# include ++#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 ++_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 ++#include ++#include ++#include ++ ++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 ++ ++_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 ++ ++_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 ++#include ++#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 ++#include ++#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 declares getopt_long. ++ For example, HP-UX 11i 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 to if __STDC__ is defined, since ++ exists even on freestanding compilers. */ ++ ++#ifdef __STDC__ ++# include ++#else ++# include ++#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 ." ++ ++_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` ++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 ++' >$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 ++' >$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 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#ifdef WITH_SECURITY ++#include ++#include ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#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 ++ */ ++ ++#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 ++ */ ++ ++#include ++#include ++#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 ++ */ ++ ++#include ++#include ++#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, ®->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 +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=
" port=" [/ protocol ] \fR. ++.BI portal= : 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 +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 +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 +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 ++ */ ++ ++#include ++#include ++#include ++#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 ++ */ ++ ++#include ++#include ++#include ++#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 ++ */ ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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=" is translated to ++ * addr=
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 ++ */ ++ ++#include ++#include ++#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 ++ */ ++ ++#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 ++ */ ++ ++#ifndef ISNS_PROTO_H ++#define ISNS_PROTO_H ++ ++#include ++ ++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 ++ * ++ * This file contains all declarations and definitions ++ * commonly required by users of libisns. ++ */ ++ ++#ifndef ISNS_H ++#define ISNS_H ++ ++#include ++#include ++#include ++ ++#include ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 \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, ®); ++ 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 :\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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef MTRACE ++# include ++#endif ++ ++#include ++#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 \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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef MTRACE ++# include ++#endif ++ ++#include ++#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 \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 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 ++# ++# 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 ++ * ++ * 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: ++ * 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. ++ * ++ * is the description of one iSNS object, using the ++ * syntax used by all other open-isns apps. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#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=: 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= 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(©, 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#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 ++ * ++ * ++ */ ++ ++#include ++#include ++#include /* for timercmp */ ++#include /* gethostname */ ++#include ++#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.] ++ * 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 ++ */ ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#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 ++ */ ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++ */ ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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?: ""); ++ 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 ++ * ++ * For now, policy is static. We can make it configurable ++ * later. ++ */ ++ ++#include ++#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("", 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 ++ */ ++ ++#include ++#include ++#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 ++ */ ++ ++#include ++#include ++#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 ++ */ ++ ++#include ++#include ++#include ++#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, ®->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, ®->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, ®->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], ++ ®->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 = ®->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, ®->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 ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#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 ++ */ ++ ++#include ++#include ++#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 ++ */ ++ ++#include ++#include ++#include ++#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 ++ */ ++ ++#ifndef ISNS_SECURITY_H ++#define ISNS_SECURITY_H ++#include "buffer.h" ++#include "util.h" ++ ++ ++#ifdef WITH_SECURITY ++#include ++#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 ++ */ ++ ++#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 ++ * ++ */ ++ ++#include ++#include ++#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: \n"); ++ } ++ ++ if (simp->is_message_attrs.ial_count == 0) { ++ fn("Message attributes: \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: \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); ++ 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 ++ */ ++ ++#include "config.h" ++#include ++#ifdef HAVE_SLP_H ++# include ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ */ ++ ++#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 ++ */ ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#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 ++ */ ++ ++#include ++#include ++#include ++#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 "/tcp, /udp, or (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(×tamp); ++ 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, ""); ++ 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, ""); ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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] = ++ 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] = ++ 0020 string : iSCSI name = "isns.client1" ++ 0021 uint32 : iSCSI node type = Initiator ++ 0024 uint32 : iSCSI node index = 5 ++object[2] = ++ 0010 ipaddr : Portal IP address = 127.0.0.1 ++ 0011 uint32 : Portal TCP/UDP port = 860/tcp ++ 0016 uint32 : Portal index = 6 ++object[3] = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= 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= 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= 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= 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= 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] = ++ 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] = ++ 0020 string : iSCSI name = "isns.client1" ++ 0021 uint32 : iSCSI node type = Initiator ++ 0024 uint32 : iSCSI node index = 6 ++object[2] = ++ 0010 ipaddr : Portal IP address = 127.1.0.1 ++ 0011 uint32 : Portal TCP/UDP port = 860/tcp ++ 0016 uint32 : Portal index = 7 ++object[3] = ++ 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] = ++ 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] = ++ 0020 string : iSCSI name = "isns.client2" ++ 0021 uint32 : iSCSI node type = Target ++ 0024 uint32 : iSCSI node index = 10 ++object[2] = ++ 0010 ipaddr : Portal IP address = 127.1.0.2 ++ 0011 uint32 : Portal TCP/UDP port = 3260/tcp ++ 0016 uint32 : Portal index = 11 ++object[3] = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= 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= 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= 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= 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= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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] = ++ 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] = ++ 0020 string : iSCSI name = "isns.client1" ++ 0021 uint32 : iSCSI node type = Initiator ++ 0024 uint32 : iSCSI node index = 6 ++object[2] = ++ 0010 ipaddr : Portal IP address = 127.1.0.1 ++ 0011 uint32 : Portal TCP/UDP port = 860/tcp ++ 0016 uint32 : Portal index = 7 ++object[3] = ++ 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] = ++ 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] = ++ 0020 string : iSCSI name = "isns.client2" ++ 0021 uint32 : iSCSI node type = Target ++ 0024 uint32 : iSCSI node index = 10 ++object[6] = ++ 0010 ipaddr : Portal IP address = 127.1.0.2 ++ 0011 uint32 : Portal TCP/UDP port = 3260/tcp ++ 0016 uint32 : Portal index = 11 ++object[7] = ++ 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] = ++ 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] = ++ 0020 string : iSCSI name = "isns.client1" ++ 0021 uint32 : iSCSI node type = Initiator ++ 0024 uint32 : iSCSI node index = 6 ++object[2] = ++ 0010 ipaddr : Portal IP address = 127.1.0.1 ++ 0011 uint32 : Portal TCP/UDP port = 860/tcp ++ 0016 uint32 : Portal index = 7 ++object[3] = ++ 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] = ++ 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] = ++ 0020 string : iSCSI name = "isns.client2" ++ 0021 uint32 : iSCSI node type = Target ++ 0024 uint32 : iSCSI node index = 10 ++object[6] = ++ 0010 ipaddr : Portal IP address = 127.1.0.2 ++ 0011 uint32 : Portal TCP/UDP port = 3260/tcp ++ 0016 uint32 : Portal index = 11 ++object[7] = ++ 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] = ++ 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] = ++ 0020 string : iSCSI name = "isns.client2" ++ 0021 uint32 : iSCSI node type = Target ++ 0024 uint32 : iSCSI node index = 10 ++object[2] = ++ 0010 ipaddr : Portal IP address = 127.1.0.2 ++ 0011 uint32 : Portal TCP/UDP port = 3260/tcp ++ 0016 uint32 : Portal index = 11 ++object[3] = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= 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= 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= 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= 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= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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= 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] = ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= 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= 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= 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= 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= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= 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= 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= 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= 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= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 0813 uint32 : DD member iSCSI index = 6 ++ 0814 string : DD member iSCSI name = "isns.client1" ++-------------- ++Object: index=14 type= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= 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= 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= 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= 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= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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] = ++ 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] = ++ 0020 string : iSCSI name = "isns.client1" ++ 0021 uint32 : iSCSI node type = Initiator ++ 0024 uint32 : iSCSI node index = 5 ++object[2] = ++ 0010 ipaddr : Portal IP address = 127.0.0.1 ++ 0011 uint32 : Portal TCP/UDP port = 860/tcp ++ 0016 uint32 : Portal index = 6 ++object[3] = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=8 type= 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= 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= 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=12 type= 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= 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= 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=12 type= 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= 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= 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= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=17 type= 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= 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= 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= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=17 type= 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= 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= 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= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=17 type= 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= 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= 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= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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= 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=17 type= 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= 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= 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= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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= 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= 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=13 type= state=limbo ++ 0020 string : iSCSI name = "isns.client1" ++ 0024 uint32 : iSCSI node index = 13 ++-------------- ++Object: index=18 type= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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= state=mature PRIVATE ++ 0001 string : Entity identifier = "CONTROL" ++ 0007 uint32 : Entity index = 1 ++-------------- ++Object: index=2 type= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=22 type= 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= 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= 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= 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= state=mature ++ 0811 uint32 : DD ID = 1 ++ 0812 string : DD name = "isns.dd1" ++ 081e uint32 : DD features = ++ 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= 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= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= 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= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=4 type= 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= 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= 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= 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= 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= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= 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= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 0024 uint32 : iSCSI node index = 3 ++-------------- ++Object: index=8 type= 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= 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= 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= 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= 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= 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= 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= 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= state=mature parent=1 PRIVATE ++ 0020 string : iSCSI name = "isns.control" ++ 0021 uint32 : iSCSI node type = ++ 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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= state=mature parent=1 ++ 0020 string : iSCSI name = "iqn.1991-05.com.microsoft:orange" ++ 0021 uint32 : iSCSI node type = Initiator ++ 0022 string : iSCSI alias = "" ++ 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 ++# ++# 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 ++# where 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 <&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 () { ++ $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 () { ++ 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 () { ++ 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 ++#include ++#include ++#include ++#include ++ ++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 = ®->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, ®); ++ ++ 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: ++ 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 = ++ :: 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 ++#include ++#include ++#include ++#include ++ ++#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 = ®->is_message_attrs; ++ STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net"); ++ ++ /* Operating attributes */ ++ attrs = ®->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, ®); ++ ++ 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 = ®->is_message_attrs; ++ STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net"); ++ ++ /* Operating attributes */ ++ attrs = ®->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, ®); ++ ++ 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 ++ 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 ++#include ++ ++#include ++#include ++#include ++#include ++ ++#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 = ®->is_message_attrs; ++ STR(ENTITY_IDENTIFIER, "blue.pauw.homeunix.net"); ++ ++ /* Operating attributes */ ++ attrs = ®->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, ®); ++ ++ 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 ++#include ++ ++#include ++#include ++#include ++#include ++ ++#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 = ®->is_message_attrs; ++ STR(ISCSI_NAME, "iqn.1991-05.com.microsoft:orange"); ++ ++ status = isns_client_call(clnt, ®); ++ 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 = ®->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, ®); ++ 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 = ®->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, ""); ++ ++ status = isns_client_call(clnt, ®); ++ 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 = ®->is_message_attrs; ++ NIL(ISCSI_NAME); ++ ++ attrs = ®->is_operating_attrs; ++ U32(ISCSI_NODE_TYPE, ISNS_ISCSI_TARGET_MASK); ++ NIL(ISCSI_NODE_TYPE); ++ ++ status = isns_client_call(clnt, ®); ++ 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 ++# ++# 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 ++# ++# 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 ++# ++# 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 ++# ++# 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 ++# ++# 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 ++# ++# 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 ++# ++# 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 ++# ++# 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 ++# ++# 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 ++# ++# 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 ++# ++# 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 ++ */ ++ ++#include ++#include ++#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 ++ */ ++ ++#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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#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 ++ */ ++ ++#ifndef UTIL_H ++#define UTIL_H ++ ++#include ++#include ++#include ++#include ++#include ++#include // 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 ++# include ++# 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 ++ */ ++ ++#include ++#include ++#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 ++ */ ++ ++#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 +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 +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 +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 +--- + 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-iscsiuio-fix-dhcpv6-transaction-id-mismatch-error.patch b/SOURCES/open-iscsi-2.0.874-30-iscsiuio-fix-dhcpv6-transaction-id-mismatch-error.patch new file mode 100644 index 0000000..3d1cc0d --- /dev/null +++ b/SOURCES/open-iscsi-2.0.874-30-iscsiuio-fix-dhcpv6-transaction-id-mismatch-error.patch @@ -0,0 +1,50 @@ +From: Chris Leech +Subject: iscsiuio: fix dhcpv6 transaction-id mismatch error + +Bugzilla: ZZZ +Upstream Status: +Build Info: XXX +Tested: + +commit 5fd6428435f3063b4e980ead1fe0671856a20183 +Author: Nilesh Javali +Date: Thu Feb 16 08:44:19 2017 -0500 + + iscsiuio: fix dhcpv6 transaction-id mismatch error + + Initialize the transaction-id within the dhcpv6 packet with + correct byte order. + + Signed-off-by: Nilesh Javali +--- + iscsiuio/src/apps/dhcpc/dhcpv6.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/iscsiuio/src/apps/dhcpc/dhcpv6.c b/iscsiuio/src/apps/dhcpc/dhcpv6.c +index a4e25f0bdbbd..461af0ec2a11 100644 +--- a/iscsiuio/src/apps/dhcpc/dhcpv6.c ++++ b/iscsiuio/src/apps/dhcpc/dhcpv6.c +@@ -153,7 +153,7 @@ static u16_t dhcpv6_init_packet(struct dhcpv6_context *context, u8_t type) + if (dhcpv6->dhcpv6_type != type) + context->dhcpv6_transaction_id++; + +- dhcpv6->dhcpv6_trans_id = context->dhcpv6_transaction_id; ++ dhcpv6->dhcpv6_trans_id = HOST_TO_NET16(context->dhcpv6_transaction_id); + dhcpv6->dhcpv6_type = type; + + /* Keep track of length of all DHCP options. */ +@@ -265,8 +265,13 @@ void ipv6_udp_handle_dhcp(struct dhcpv6_context *context) + dhcpv6 = (union dhcpv6_hdr *)((u8_t *)context->udp + + sizeof(struct udp_hdr)); + +- if (dhcpv6->dhcpv6_trans_id != context->dhcpv6_transaction_id) ++ if (dhcpv6->dhcpv6_trans_id != ++ HOST_TO_NET16(context->dhcpv6_transaction_id)) { ++ LOG_ERR("DHCPv6 transaction-id error, sent %x, received %x", ++ HOST_TO_NET16(context->dhcpv6_transaction_id), ++ dhcpv6->dhcpv6_trans_id); + return; ++ } + + dhcpv6_len = + NET_TO_HOST16(context->udp->length) - sizeof(struct udp_hdr); 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 +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-31-iscsiuio-serialize-xmit_mutex-lock-to-prevent-iscsiuio-seg-fault.patch b/SOURCES/open-iscsi-2.0.874-31-iscsiuio-serialize-xmit_mutex-lock-to-prevent-iscsiuio-seg-fault.patch new file mode 100644 index 0000000..1b92687 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.874-31-iscsiuio-serialize-xmit_mutex-lock-to-prevent-iscsiuio-seg-fault.patch @@ -0,0 +1,185 @@ +From: Chris Leech +Subject: iscsiuio: serialize xmit_mutex lock to prevent iscsiuio seg fault + +Bugzilla: ZZZ +Upstream Status: +Build Info: XXX +Tested: + +commit a1be9c4ec348f87923f63ce2dbc23893a3b9e45c +Author: Nilesh Javali +Date: Thu May 18 23:04:20 2017 +0530 + + iscsiuio: serialize xmit_mutex lock to prevent iscsiuio seg fault + + Signed-off-by: Nilesh Javali +--- + iscsiuio/src/unix/libs/bnx2x.c | 24 +++++++++++------------- + iscsiuio/src/unix/libs/cnic.c | 9 +++------ + iscsiuio/src/unix/libs/qedi.c | 19 +++++++++++-------- + 3 files changed, 25 insertions(+), 27 deletions(-) + +diff --git a/iscsiuio/src/unix/libs/bnx2x.c b/iscsiuio/src/unix/libs/bnx2x.c +index 19cbcecaacca..1e8f532edcdf 100644 +--- a/iscsiuio/src/unix/libs/bnx2x.c ++++ b/iscsiuio/src/unix/libs/bnx2x.c +@@ -1316,7 +1316,6 @@ void bnx2x_start_xmit(nic_t *nic, size_t len, u16_t vlan_id) + if ((rx_bd->addr_hi == 0) && (rx_bd->addr_lo == 0)) { + LOG_PACKET(PFX "%s: trying to transmit when device is closed", + nic->log_name); +- pthread_mutex_unlock(&nic->xmit_mutex); + return; + } + +@@ -1343,12 +1342,9 @@ void bnx2x_start_xmit(nic_t *nic, size_t len, u16_t vlan_id) + (bp->tx_bd_prod << 16)); + bnx2x_flush_doorbell(bp, bp->tx_doorbell); + } else { +- /* If the doorbell is not rung, the packet will not +- get sent. Hence, the xmit_mutex lock will not +- get freed. +- */ +- pthread_mutex_unlock(&nic->xmit_mutex); ++ LOG_ERR(PFX "Pkt transmission failed."); + } ++ + LOG_PACKET(PFX "%s: sent %d bytes using bp->tx_prod: %d", + nic->log_name, len, bp->tx_prod); + } +@@ -1412,6 +1408,8 @@ int bnx2x_write(nic_t *nic, nic_interface_t *nic_iface, packet_t *pkt) + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bd_prod); + ++ pthread_mutex_unlock(&nic->xmit_mutex); ++ + return 0; + } + +@@ -1560,17 +1558,16 @@ static int bnx2x_clear_tx_intr(nic_t *nic) + hw_cons = bp->get_tx_cons(bp); + + 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 "bnx2x tx lock with prod == cons"); +- +- pthread_mutex_unlock(&nic->xmit_mutex); ++ if (bp->tx_cons == bp->tx_prod) + return 0; +- } + return -EAGAIN; + } + ++ if (pthread_mutex_trylock(&nic->xmit_mutex)) { ++ LOG_ERR(PFX "%s: unable to get xmit_mutex.", nic->log_name); ++ return -EINVAL; ++ } ++ + LOG_PACKET(PFX "%s: clearing tx interrupt [%d %d]", + nic->log_name, bp->tx_cons, hw_cons); + bp->tx_cons = hw_cons; +@@ -1600,6 +1597,7 @@ static int bnx2x_clear_tx_intr(nic_t *nic) + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bd_prod); + ++ pthread_mutex_unlock(&nic->xmit_mutex); + return 0; + } + +diff --git a/iscsiuio/src/unix/libs/cnic.c b/iscsiuio/src/unix/libs/cnic.c +index 5d60f898ad57..a009f25f0814 100644 +--- a/iscsiuio/src/unix/libs/cnic.c ++++ b/iscsiuio/src/unix/libs/cnic.c +@@ -141,6 +141,7 @@ static int cnic_arp_send(nic_t *nic, nic_interface_t *nic_iface, int fd, + memcpy(&addr.s_addr, &dst_ip, sizeof(addr.s_addr)); + LOG_DEBUG(PFX "%s: Sent cnic arp request for IP: %s", + nic->log_name, addr_str); ++ pthread_mutex_unlock(&nic->xmit_mutex); + + return 0; + } +@@ -204,6 +205,8 @@ static int cnic_neigh_soliciation_send(nic_t *nic, + LOG_DEBUG(PFX "%s: Sent cnic ICMPv6 neighbor request %s", + nic->log_name, addr_str); + ++ pthread_mutex_unlock(&nic->xmit_mutex); ++ + return 0; + } + +@@ -433,9 +436,6 @@ done: + rc = -EIO; + } + +- if (status != 0 || rc != 0) +- pthread_mutex_unlock(&nic->xmit_mutex); +- + if (ev) { + cnic_nl_neigh_rsp(nic, fd, ev, path, mac_addr, + nic_iface, status, AF_INET); +@@ -632,9 +632,6 @@ done: + rc = -EIO; + } + +- if (status != 0 || rc != 0) +- pthread_mutex_unlock(&nic->xmit_mutex); +- + if (ev) { + cnic_nl_neigh_rsp(nic, fd, ev, path, mac_addr, + nic_iface, status, AF_INET6); +diff --git a/iscsiuio/src/unix/libs/qedi.c b/iscsiuio/src/unix/libs/qedi.c +index c2096e59dad1..c6ff6e7724a3 100644 +--- a/iscsiuio/src/unix/libs/qedi.c ++++ b/iscsiuio/src/unix/libs/qedi.c +@@ -887,7 +887,6 @@ void qedi_start_xmit(nic_t *nic, size_t len, u16_t vlan_id) + nic->log_name, len, bp->tx_prod); + } else { + LOG_ERR(PFX "Pkt transmission failed: %d", rc); +- pthread_mutex_unlock(&nic->xmit_mutex); + } + + free(ubuf); +@@ -950,6 +949,10 @@ int qedi_write(nic_t *nic, nic_interface_t *nic_iface, packet_t *pkt) + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bd_prod); + ++ LOG_DEBUG(PFX "%s: host:%d - releasing xmit mutex", ++ nic->log_name, nic->host_no); ++ pthread_mutex_unlock(&nic->xmit_mutex); ++ + return 0; + } + +@@ -1059,17 +1062,16 @@ static int qedi_clear_tx_intr(nic_t *nic) + 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); ++ if (bp->tx_cons == bp->tx_prod) + return 0; +- } + return -EAGAIN; + } + ++ if (pthread_mutex_trylock(&nic->xmit_mutex)) { ++ LOG_ERR(PFX "%s: unable to get xmit_mutex.", nic->log_name); ++ return -EINVAL; ++ } ++ + LOG_PACKET(PFX "%s: clearing tx interrupt [%d %d]", + nic->log_name, bp->tx_cons, hw_cons); + bp->tx_cons = hw_cons; +@@ -1099,6 +1101,7 @@ static int qedi_clear_tx_intr(nic_t *nic) + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bd_prod); + ++ pthread_mutex_unlock(&nic->xmit_mutex); + return 0; + } + diff --git a/SOURCES/open-iscsi-2.0.874-32-iscsiuio-allow-ARP-for-non-matching-src-and-dst-addresses.patch b/SOURCES/open-iscsi-2.0.874-32-iscsiuio-allow-ARP-for-non-matching-src-and-dst-addresses.patch new file mode 100644 index 0000000..fc92428 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.874-32-iscsiuio-allow-ARP-for-non-matching-src-and-dst-addresses.patch @@ -0,0 +1,40 @@ +From: Chris Leech +Subject: iscsiuio: allow ARP for non-matching src and dst addresses + +Bugzilla: ZZZ +Upstream Status: +Build Info: XXX +Tested: + +commit e43d687dc23e66c609491f9bfa8d8f29be7ef72d +Author: Nilesh Javali +Date: Thu Feb 22 07:25:58 2018 -0500 + + iscsiuio: allow ARP for non-matching src and dst addresses + + For source and destination IP addresses in different + networks, continue with the ARP retries and further login process + instead of assuming abrupt failure. iSCSI offload adapters may not rely on + netmask information for successful iSCSI target login. + + Signed-off-by: Nilesh Javali +--- + iscsiuio/src/unix/libs/cnic.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/iscsiuio/src/unix/libs/cnic.c b/iscsiuio/src/unix/libs/cnic.c +index a009f25f0814..32166edf243f 100644 +--- a/iscsiuio/src/unix/libs/cnic.c ++++ b/iscsiuio/src/unix/libs/cnic.c +@@ -362,9 +362,8 @@ int cnic_handle_ipv4_iscsi_path_req(nic_t *nic, int fd, + &nic_iface->ustack.default_route_addr, + sizeof(dst_addr)); + } else { +- arp_retry = MAX_ARP_RETRY; +- LOG_DEBUG(PFX "%s: no default", nic->log_name); +- goto done; ++ LOG_DEBUG(PFX "%s: no default route address", ++ nic->log_name); + } + } + arp_retry = 0; diff --git a/SOURCES/open-iscsi-2.0.874-33-iscsiuio-v0.7.8.4.patch b/SOURCES/open-iscsi-2.0.874-33-iscsiuio-v0.7.8.4.patch new file mode 100644 index 0000000..46b8cb6 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.874-33-iscsiuio-v0.7.8.4.patch @@ -0,0 +1,106 @@ +From: Chris Leech +Subject: iscsiuio: v0.7.8.4 + +Bugzilla: ZZZ +Upstream Status: +Build Info: XXX +Tested: + +commit 5481f86e46d1668917bbf41ed2355b6eb558de69 +Author: Nilesh Javali +Date: Thu Feb 22 08:04:13 2018 -0500 + + iscsiuio: v0.7.8.4 + + Signed-off-by: Nilesh Javali +--- + iscsiuio/README | 4 ++-- + iscsiuio/RELEASE.TXT | 39 +++++++++++++++++++++++++++++++++++++-- + iscsiuio/configure.ac | 4 ++-- + 3 files changed, 41 insertions(+), 6 deletions(-) + +diff --git a/iscsiuio/README b/iscsiuio/README +index ca2da16a7342..9cbf7ce6717a 100644 +--- a/iscsiuio/README ++++ b/iscsiuio/README +@@ -1,6 +1,6 @@ + Iscsiuio Userspace Tool +-Version 0.7.8.3 +-Sept 28, 2016 ++Version 0.7.8.4 ++Feb 22, 2018 + ------------------------------------------------------ + + 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 69c5b5f6bd61..ca51b26338a1 100644 +--- a/iscsiuio/RELEASE.TXT ++++ b/iscsiuio/RELEASE.TXT +@@ -1,7 +1,7 @@ + Release Notes + QLogic uIP Linux Driver +- Version 0.7.8.3 +- 9/28/2016 ++ Version 0.7.8.4 ++ 2/22/2018 + + QLogic Corporation + 26650 Aliso Viejo Pkwy, +@@ -12,6 +12,41 @@ + All rights reserved + + ++ ++uIP v0.7.8.4 (Feb 22, 2018) ++======================================================= ++ Fixes: ++ ------- ++ 1. Problem: CQ95605: iSCSI BFS in DHCP config intermittently fails to boot ++ into the OS when source and destination addresses are in ++ different networks. ++ Change: Allow ARP for non-matching source and destination addresses. ++ For source and destination IP addresses in different networks, ++ continue with the ARP retries and further login process ++ instead of assuming abrupt failure. iSCSI offload adapters ++ may not rely on netmask information for successful iSCSI ++ target login. ++ Impact: All ++ ++uIP v0.7.8.3 (May 18, 2017) ++======================================================= ++ Fixes: ++ ------- ++ 1. Problem: CQ93985: iscsiuio seg faults if discovery done to not ++ reachable target ++ Change: Serialize xmit_mutex lock to prevent iscsiuio seg fault. ++ Impact: All ++ ++ 2. Problem: CQ91497 - Initiator fails to acquire IPv6 DHCP address ++ from the DHCP server ++ Change: Initialize the transaction-id within the dhcpv6 packet with ++ correct byte order, to fix the trans-id mismatch error. ++ Impact: All ++ ++ 3. Problem: Missing qedi ping transport hook ++ Change: Add qedi ping transport hook ++ Impact: 10/25/40/50GGbE Controller (iSCSI) ++ + uIP v0.7.8.3 (Sept 28, 2016) + ======================================================= + Enhancements +diff --git a/iscsiuio/configure.ac b/iscsiuio/configure.ac +index 075d07d04f34..fa67ea74c47a 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.3 ++VERSION=0.7.8.4 + +-AC_INIT([iscsiuio], [0.7.8.3], [QLogic-Storage-Upstream@cavium.com]) ++AC_INIT([iscsiuio], [0.7.8.4], [QLogic-Storage-Upstream@cavium.com]) + + AM_INIT_AUTOMAKE + AC_CONFIG_HEADER(config.h) diff --git a/SOURCES/open-iscsi-2.0.874-35-iscsiuio-fix-long-options.patch b/SOURCES/open-iscsi-2.0.874-35-iscsiuio-fix-long-options.patch new file mode 100644 index 0000000..f6261b0 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.874-35-iscsiuio-fix-long-options.patch @@ -0,0 +1,82 @@ +From 06851794fe0475410f516e874ee0c67db4a6784a Mon Sep 17 00:00:00 2001 +From: Andrew Patterson +Date: Wed, 31 May 2017 10:30:21 -0600 +Subject: [PATCH] iscsiuio: fix long options + +Fix broken long command-line options in iscsiuio. +--- + iscsiuio/docs/iscsiuio.8 | 10 +++++----- + iscsiuio/src/unix/main.c | 12 +++++++----- + 2 files changed, 12 insertions(+), 10 deletions(-) + +diff --git a/iscsiuio/docs/iscsiuio.8 b/iscsiuio/docs/iscsiuio.8 +index 8b0577a53842..eda20c76637f 100644 +--- a/iscsiuio/docs/iscsiuio.8 ++++ b/iscsiuio/docs/iscsiuio.8 +@@ -45,7 +45,7 @@ on how to configure network protocol and address. + .SH PARAMETERS + There are very few parameters when running this application. + .TP +-.BI -d ++.BI -d|--debug + This is to enable debug mode where debug messages will be sent to stdout + The following debug modes are supported + .P +@@ -61,20 +61,20 @@ ERROR 1 - Only print critical errors + .PP + .TP + .TP +-.BI -f ++.BI -f|--foreground + This is to enable foreground mode so that this application doesn't get sent + into the background. + .PP + .TP +-.BI -v ++.BI -v|--version + This is to print the version. + .PP + .TP +-.BI -p ++.BI -p|--pid + Use pidfile (default /var/run/iscsiuio.pid ) + .PP + .TP +-.BI -h ++.BI -h|--help + Display this help and exit. + + +diff --git a/iscsiuio/src/unix/main.c b/iscsiuio/src/unix/main.c +index c1a72d800741..5cccc76ed220 100644 +--- a/iscsiuio/src/unix/main.c ++++ b/iscsiuio/src/unix/main.c +@@ -82,10 +82,12 @@ static const char default_pid_filepath[] = "/var/run/iscsiuio.pid"; + * Global Variables + ******************************************************************************/ + static const struct option long_options[] = { +- {"debug", 0, 0, 0}, +- {"version", 0, 0, 0}, +- {"help", 0, 0, 0}, +- {0, 0, 0, 0} ++ {"foreground", no_argument, NULL, 'f'}, ++ {"debug", required_argument, NULL, 'd'}, ++ {"pid", required_argument, NULL, 'p'}, ++ {"version", no_argument, NULL, 'v'}, ++ {"help", no_argument, NULL, 'h'}, ++ {NULL, no_argument, NULL, 0} + }; + + struct options opt = { +@@ -172,7 +174,7 @@ static void main_usage() + printf("iscsiuio daemon.\n" + "-f, --foreground make the program run in the foreground\n" + "-d, --debug debuglevel print debugging information\n" +- "-p, --pid=pidfile use pid file (default %s).\n" ++ "-p, --pid pidfile use pid file (default %s).\n" + "-h, --help display this help and exit\n" + "-v, --version display version and exit\n", + default_pid_filepath); +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.874-38-Update-bnx2.c.patch b/SOURCES/open-iscsi-2.0.874-38-Update-bnx2.c.patch new file mode 100644 index 0000000..e1f33c7 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.874-38-Update-bnx2.c.patch @@ -0,0 +1,38 @@ +From 5cecffad475735d672305fcf0163fd79d3b93ff9 Mon Sep 17 00:00:00 2001 +From: Edward Kigwana +Date: Fri, 11 Aug 2017 21:50:40 +0000 +Subject: [PATCH] Update bnx2.c +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Release notes for glibc-2.25 state: + +The inclusion of by is deprecated. +This means that in a future release, the macros “major”, “minor”, and +“makedev” will only be available from . + +These macros are not part of POSIX nor XSI, and their names frequently +collide with user code; see for instance glibc bug 19239 and Red Hat +bug 130601. includes under _GNU_SOURCE, and +C++ code presently cannot avoid being compiled under _GNU_SOURCE, +exacerbating the problem. +--- + iscsiuio/src/unix/libs/bnx2.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/iscsiuio/src/unix/libs/bnx2.c b/iscsiuio/src/unix/libs/bnx2.c +index 457bdba283e8..c68131c0ca24 100644 +--- a/iscsiuio/src/unix/libs/bnx2.c ++++ b/iscsiuio/src/unix/libs/bnx2.c +@@ -41,6 +41,7 @@ + #include + #include + #include ++#include + #include + #include + #include +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.874-39-Update-bnx2x.c.patch b/SOURCES/open-iscsi-2.0.874-39-Update-bnx2x.c.patch new file mode 100644 index 0000000..96d3630 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.874-39-Update-bnx2x.c.patch @@ -0,0 +1,38 @@ +From fea8d2e506a40f80f9062aa9c98f403518782f27 Mon Sep 17 00:00:00 2001 +From: Edward Kigwana +Date: Fri, 11 Aug 2017 21:51:52 +0000 +Subject: [PATCH] Update bnx2x.c +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Release notes for glibc-2.25 state: + +The inclusion of by is deprecated. +This means that in a future release, the macros “major”, “minor”, and +“makedev” will only be available from . + +These macros are not part of POSIX nor XSI, and their names frequently +collide with user code; see for instance glibc bug 19239 and Red Hat +bug 130601. includes under _GNU_SOURCE, and +C++ code presently cannot avoid being compiled under _GNU_SOURCE, +exacerbating the problem. +--- + iscsiuio/src/unix/libs/bnx2x.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/iscsiuio/src/unix/libs/bnx2x.c b/iscsiuio/src/unix/libs/bnx2x.c +index 1e8f532edcdf..3df6d5fdbfa2 100644 +--- a/iscsiuio/src/unix/libs/bnx2x.c ++++ b/iscsiuio/src/unix/libs/bnx2x.c +@@ -45,6 +45,7 @@ + #include + #include + #include ++#include + #include + #include + #include +-- +2.17.2 + 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 +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 +--- + 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 +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 +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; ihostno, 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 +Date: Fri, 11 Nov 2016 08:17:50 +0200 +Subject: iscsid: Changes to support the new qedi transport + +Signed-off-by: Manish Rangankar +Signed-off-by: Adheer Chandravanshi +Signed-off-by: Nilesh Javali +--- + 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 +Date: Fri, 11 Nov 2016 08:17:51 +0200 +Subject: iscsiuio: Add support for the new qedi transport + +Signed-off-by: Manish Rangankar +Signed-off-by: Adheer Chandravanshi +Signed-off-by: Nilesh Javali +--- + 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 + #include + #include ++#include + + #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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 +Date: Fri, 11 Nov 2016 08:17:52 +0200 +Subject: iscsiuio: v0.7.8.3 + +Signed-off-by: Manish Rangankar +Signed-off-by: Adheer Chandravanshi +Signed-off-by: Nilesh Javali +--- + 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 +Date: Tue, 28 Feb 2017 19:34:03 -0800 +Subject: iscsid: reset head on wrap when buffer empty + +Reported-By: David Jeffery +--- + 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/SOURCES/open-iscsi-2.0.875-10-Include-sys-sysmacros.h-to-properly-define-minor.patch b/SOURCES/open-iscsi-2.0.875-10-Include-sys-sysmacros.h-to-properly-define-minor.patch new file mode 100644 index 0000000..bc4ef65 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-10-Include-sys-sysmacros.h-to-properly-define-minor.patch @@ -0,0 +1,24 @@ +From 6d68ef5871c94c6ebbbe6e6b1fe0bc2dce711052 Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Sat, 2 Dec 2017 14:19:26 -0800 +Subject: [PATCH] Include to properly define minor() + +--- + iscsiuio/src/unix/libs/qedi.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/iscsiuio/src/unix/libs/qedi.c b/iscsiuio/src/unix/libs/qedi.c +index c6ff6e7724a3..b81fecdfe78f 100644 +--- a/iscsiuio/src/unix/libs/qedi.c ++++ b/iscsiuio/src/unix/libs/qedi.c +@@ -58,6 +58,7 @@ + #include + #include + #include ++#include + + #include "config.h" + +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-11-Declare-inline-best_match_bufcmp-as-static.patch b/SOURCES/open-iscsi-2.0.875-11-Declare-inline-best_match_bufcmp-as-static.patch new file mode 100644 index 0000000..d123637 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-11-Declare-inline-best_match_bufcmp-as-static.patch @@ -0,0 +1,27 @@ +From ec9865c5b2a05908dff774eda352d84d6a001e1a Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Sat, 2 Dec 2017 14:24:52 -0800 +Subject: [PATCH] Declare inline best_match_bufcmp() as static. + +The 7.2.1 version of gcc seems to be more strict +about this. See https://gcc.gnu.org/onlinedocs/gcc/Inline.html +--- + iscsiuio/src/uip/ipv6.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/iscsiuio/src/uip/ipv6.c b/iscsiuio/src/uip/ipv6.c +index ced98a678899..05efa73fcfdd 100644 +--- a/iscsiuio/src/uip/ipv6.c ++++ b/iscsiuio/src/uip/ipv6.c +@@ -49,7 +49,7 @@ + #include "dhcpv6.h" + #include "ping.h" + +-inline int best_match_bufcmp(u8_t *a, u8_t *b, int len) ++static inline int best_match_bufcmp(u8_t *a, u8_t *b, int len) + { + int i; + +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-14-Check-for-root-peer-user-for-iscsiuio-IPC.patch b/SOURCES/open-iscsi-2.0.875-14-Check-for-root-peer-user-for-iscsiuio-IPC.patch new file mode 100644 index 0000000..951e157 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-14-Check-for-root-peer-user-for-iscsiuio-IPC.patch @@ -0,0 +1,129 @@ +From e313bd648a4c8a9526421e270eb597a5de1e0c7f Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Fri, 15 Dec 2017 10:36:11 -0800 +Subject: [PATCH] Check for root peer user for iscsiuio IPC + +This fixes a possible vulnerability where a non-root +process could connect with iscsiuio. Fouund by Qualsys. +--- + iscsiuio/src/unix/Makefile.am | 3 ++- + iscsiuio/src/unix/iscsid_ipc.c | 47 ++++++++++++++++++++++++++++++++++ + 2 files changed, 49 insertions(+), 1 deletion(-) + +diff --git a/iscsiuio/src/unix/Makefile.am b/iscsiuio/src/unix/Makefile.am +index 71d54633a764..a989ef029b59 100644 +--- a/iscsiuio/src/unix/Makefile.am ++++ b/iscsiuio/src/unix/Makefile.am +@@ -20,7 +20,8 @@ iscsiuio_SOURCES = build_date.c \ + nic_utils.c \ + packet.c \ + iscsid_ipc.c \ +- ping.c ++ ping.c \ ++ ${top_srcdir}/../utils/sysdeps/sysdeps.c + + iscsiuio_CFLAGS = $(AM_CFLAGS) \ + $(LIBNL_CFLAGS) \ +diff --git a/iscsiuio/src/unix/iscsid_ipc.c b/iscsiuio/src/unix/iscsid_ipc.c +index 658362899234..64762654c523 100644 +--- a/iscsiuio/src/unix/iscsid_ipc.c ++++ b/iscsiuio/src/unix/iscsid_ipc.c +@@ -37,6 +37,8 @@ + * + */ + ++#define _GNU_SOURCE ++ + #include + #include + #include +@@ -47,6 +49,8 @@ + #include + #include + #include ++#include ++#include + + #define PFX "iscsi_ipc " + +@@ -61,6 +65,7 @@ + #include "iscsid_ipc.h" + #include "uip.h" + #include "uip_mgmt_ipc.h" ++#include "sysdeps.h" + + #include "logger.h" + #include "uip.h" +@@ -102,6 +107,7 @@ struct iface_rec_decode { + uint16_t mtu; + }; + ++#define PEERUSER_MAX 64 + + /****************************************************************************** + * Globals +@@ -1024,6 +1030,40 @@ static void iscsid_loop_close(void *arg) + LOG_INFO(PFX "iSCSI daemon socket closed"); + } + ++/* ++ * check that the peer user is privilidged ++ * ++ * return 1 if peer is ok else 0 ++ * ++ * XXX: this function is copied from iscsid_ipc.c and should be ++ * moved into a common library ++ */ ++static int ++mgmt_peeruser(int sock, char *user) ++{ ++ struct ucred peercred; ++ socklen_t so_len = sizeof(peercred); ++ struct passwd *pass; ++ ++ errno = 0; ++ if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, ++ &so_len) != 0 || so_len != sizeof(peercred)) { ++ /* We didn't get a valid credentials struct. */ ++ LOG_ERR(PFX "peeruser_unux: error receiving credentials: %m"); ++ return 0; ++ } ++ ++ pass = getpwuid(peercred.uid); ++ if (pass == NULL) { ++ LOG_ERR(PFX "peeruser_unix: unknown local user with uid %d", ++ (int) peercred.uid); ++ return 0; ++ } ++ ++ strlcpy(user, pass->pw_name, PEERUSER_MAX); ++ return 1; ++} ++ + /** + * iscsid_loop() - This is the function which will process the broadcast + * messages from iscsid +@@ -1033,6 +1073,7 @@ static void *iscsid_loop(void *arg) + { + int rc; + sigset_t set; ++ char user[PEERUSER_MAX]; + + pthread_cleanup_push(iscsid_loop_close, arg); + +@@ -1072,6 +1113,12 @@ static void *iscsid_loop(void *arg) + continue; + } + ++ if (!mgmt_peeruser(iscsid_opts.fd, user) || strncmp(user, "root", PEERUSER_MAX)) { ++ close(s2); ++ LOG_ERR(PFX "Access error: non-administrative connection rejected"); ++ break; ++ } ++ + process_iscsid_broadcast(s2); + close(s2); + } +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-15-iscsiuio-should-ignore-bogus-iscsid-broadcast-packets.patch b/SOURCES/open-iscsi-2.0.875-15-iscsiuio-should-ignore-bogus-iscsid-broadcast-packets.patch new file mode 100644 index 0000000..c958506 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-15-iscsiuio-should-ignore-bogus-iscsid-broadcast-packets.patch @@ -0,0 +1,33 @@ +From b9c33683bdc0aed28ffe31c3f3d50bf5cdf519ea Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Fri, 15 Dec 2017 10:37:56 -0800 +Subject: [PATCH] iscsiuio should ignore bogus iscsid broadcast packets + +When iscsiuio is receiving broadcast packets from iscsid, +if the 'payload_len', carried in the packet, is too +large then ignore the packet and print a message. +Found by Qualsys. +--- + iscsiuio/src/unix/iscsid_ipc.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/iscsiuio/src/unix/iscsid_ipc.c b/iscsiuio/src/unix/iscsid_ipc.c +index 64762654c523..b5d7051b0558 100644 +--- a/iscsiuio/src/unix/iscsid_ipc.c ++++ b/iscsiuio/src/unix/iscsid_ipc.c +@@ -945,6 +945,12 @@ int process_iscsid_broadcast(int s2) + + cmd = data->header.command; + payload_len = data->header.payload_len; ++ if (payload_len > sizeof(data->u)) { ++ LOG_ERR(PFX "Data payload length too large (%d). Corrupt payload?", ++ payload_len); ++ rc = -EINVAL; ++ goto error; ++ } + + LOG_DEBUG(PFX "recv iscsid request: cmd: %d, payload_len: %d", + cmd, payload_len); +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-16-Ensure-all-fields-in-iscsiuio-IPC-response-are-set.patch b/SOURCES/open-iscsi-2.0.875-16-Ensure-all-fields-in-iscsiuio-IPC-response-are-set.patch new file mode 100644 index 0000000..971e2bb --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-16-Ensure-all-fields-in-iscsiuio-IPC-response-are-set.patch @@ -0,0 +1,28 @@ +From be58eed849f5457bb49b79e94aa6a26971ba6deb Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Fri, 15 Dec 2017 11:11:17 -0800 +Subject: [PATCH] Ensure all fields in iscsiuio IPC response are set + +Make sure all fields in the response strcuture are set, +or info from the stack can be leaked to our caller. +Found by Qualsys. +--- + iscsiuio/src/unix/iscsid_ipc.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/iscsiuio/src/unix/iscsid_ipc.c b/iscsiuio/src/unix/iscsid_ipc.c +index b5d7051b0558..c3b10e03774a 100644 +--- a/iscsiuio/src/unix/iscsid_ipc.c ++++ b/iscsiuio/src/unix/iscsid_ipc.c +@@ -955,6 +955,8 @@ int process_iscsid_broadcast(int s2) + LOG_DEBUG(PFX "recv iscsid request: cmd: %d, payload_len: %d", + cmd, payload_len); + ++ memset(&rsp, 0, sizeof(rsp)); ++ + switch (cmd) { + case ISCSID_UIP_IPC_GET_IFACE: + size = fread(&data->u.iface_rec, payload_len, 1, fd); +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-17-Do-not-double-close-IPC-file-stream-to-iscsid.patch b/SOURCES/open-iscsi-2.0.875-17-Do-not-double-close-IPC-file-stream-to-iscsid.patch new file mode 100644 index 0000000..43dbfb0 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-17-Do-not-double-close-IPC-file-stream-to-iscsid.patch @@ -0,0 +1,56 @@ +From 5504053cc08df38d8d85032fa1691e363dfcfb92 Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Fri, 15 Dec 2017 11:13:29 -0800 +Subject: [PATCH] Do not double-close IPC file stream to iscsid + +A double-close of a file descriptor and its associated FILE stream +can be an issue in multi-threaded cases. Found by Qualsys. +--- + iscsiuio/src/unix/iscsid_ipc.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/iscsiuio/src/unix/iscsid_ipc.c b/iscsiuio/src/unix/iscsid_ipc.c +index c3b10e03774a..4a6687b32a17 100644 +--- a/iscsiuio/src/unix/iscsid_ipc.c ++++ b/iscsiuio/src/unix/iscsid_ipc.c +@@ -908,6 +908,9 @@ early_exit: + /** + * process_iscsid_broadcast() - This function is used to process the + * broadcast messages from iscsid ++ * ++ * s2 is an open file descriptor, which ++ * must not be left open upon return + */ + int process_iscsid_broadcast(int s2) + { +@@ -923,6 +926,7 @@ int process_iscsid_broadcast(int s2) + if (fd == NULL) { + LOG_ERR(PFX "Couldn't open file descriptor: %d(%s)", + errno, strerror(errno)); ++ close(s2); + return -EIO; + } + +@@ -1025,7 +1029,8 @@ int process_iscsid_broadcast(int s2) + } + + error: +- free(data); ++ if (data) ++ free(data); + fclose(fd); + + return rc; +@@ -1127,8 +1132,8 @@ static void *iscsid_loop(void *arg) + break; + } + ++ /* this closes the file descriptor s2 */ + process_iscsid_broadcast(s2); +- close(s2); + } + + pthread_cleanup_pop(0); +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-18-Ensure-strings-from-peer-are-copied-correctly.patch b/SOURCES/open-iscsi-2.0.875-18-Ensure-strings-from-peer-are-copied-correctly.patch new file mode 100644 index 0000000..a2ac39b --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-18-Ensure-strings-from-peer-are-copied-correctly.patch @@ -0,0 +1,72 @@ +From 85f647c4300a888bb6cbc27f33138549cab617e3 Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Fri, 15 Dec 2017 11:15:26 -0800 +Subject: [PATCH] Ensure strings from peer are copied correctly. + +The method of using strlen() and strcpy()/strncpy() has +a couple of holes. Do not try to measure the length of +strings supplied from peer, and ensure copied strings are +NULL-terminated. Use the new strlcpy() instead. +Found by Qualsys. +--- + iscsiuio/src/unix/iscsid_ipc.c | 24 ++++++------------------ + 1 file changed, 6 insertions(+), 18 deletions(-) + +diff --git a/iscsiuio/src/unix/iscsid_ipc.c b/iscsiuio/src/unix/iscsid_ipc.c +index 4a6687b32a17..8478f9a411a3 100644 +--- a/iscsiuio/src/unix/iscsid_ipc.c ++++ b/iscsiuio/src/unix/iscsid_ipc.c +@@ -147,10 +147,7 @@ static int decode_cidr(char *in_ipaddr_str, struct iface_rec_decode *ird) + struct in_addr ia; + struct in6_addr ia6; + +- if (strlen(in_ipaddr_str) > NI_MAXHOST) +- strncpy(ipaddr_str, in_ipaddr_str, NI_MAXHOST); +- else +- strcpy(ipaddr_str, in_ipaddr_str); ++ strlcpy(ipaddr_str, in_ipaddr_str, NI_MAXHOST); + + /* Find the CIDR if any */ + tmp = strchr(ipaddr_str, '/'); +@@ -282,22 +279,16 @@ static int decode_iface(struct iface_rec_decode *ird, struct iface_rec *rec) + + /* For LL on, ignore the IPv6 addr in the iface */ + if (ird->linklocal_autocfg == IPV6_LL_AUTOCFG_OFF) { +- if (strlen(rec->ipv6_linklocal) > NI_MAXHOST) +- strncpy(ipaddr_str, rec->ipv6_linklocal, +- NI_MAXHOST); +- else +- strcpy(ipaddr_str, rec->ipv6_linklocal); ++ strlcpy(ipaddr_str, rec->ipv6_linklocal, ++ NI_MAXHOST); + inet_pton(AF_INET6, ipaddr_str, + &ird->ipv6_linklocal); + } + + /* For RTR on, ignore the IPv6 addr in the iface */ + if (ird->router_autocfg == IPV6_RTR_AUTOCFG_OFF) { +- if (strlen(rec->ipv6_router) > NI_MAXHOST) +- strncpy(ipaddr_str, rec->ipv6_router, +- NI_MAXHOST); +- else +- strcpy(ipaddr_str, rec->ipv6_router); ++ strlcpy(ipaddr_str, rec->ipv6_router, ++ NI_MAXHOST); + inet_pton(AF_INET6, ipaddr_str, + &ird->ipv6_router); + } +@@ -311,10 +302,7 @@ static int decode_iface(struct iface_rec_decode *ird, struct iface_rec *rec) + calculate_default_netmask( + ird->ipv4_addr.s_addr); + +- if (strlen(rec->gateway) > NI_MAXHOST) +- strncpy(ipaddr_str, rec->gateway, NI_MAXHOST); +- else +- strcpy(ipaddr_str, rec->gateway); ++ strlcpy(ipaddr_str, rec->gateway, NI_MAXHOST); + inet_pton(AF_INET, ipaddr_str, &ird->ipv4_gateway); + } + } else { +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-19-Skip-useless-strcopy-and-validate-CIDR-length.patch b/SOURCES/open-iscsi-2.0.875-19-Skip-useless-strcopy-and-validate-CIDR-length.patch new file mode 100644 index 0000000..40e37c6 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-19-Skip-useless-strcopy-and-validate-CIDR-length.patch @@ -0,0 +1,38 @@ +From a7a96131bd2ea342f6def0e46be514baf8037ae8 Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Fri, 15 Dec 2017 11:18:35 -0800 +Subject: [PATCH] Skip useless strcopy, and validate CIDR length + +Remove a useless strcpy() that copies a string onto itself, +and ensure the CIDR length "keepbits" is not negative. +Found by Qualsys. +--- + iscsiuio/src/unix/iscsid_ipc.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/iscsiuio/src/unix/iscsid_ipc.c b/iscsiuio/src/unix/iscsid_ipc.c +index 8478f9a411a3..4e3d065667c9 100644 +--- a/iscsiuio/src/unix/iscsid_ipc.c ++++ b/iscsiuio/src/unix/iscsid_ipc.c +@@ -143,7 +143,7 @@ static int decode_cidr(char *in_ipaddr_str, struct iface_rec_decode *ird) + char *tmp, *tok; + char ipaddr_str[NI_MAXHOST]; + char str[INET6_ADDRSTRLEN]; +- int keepbits = 0; ++ unsigned long keepbits = 0; + struct in_addr ia; + struct in6_addr ia6; + +@@ -156,8 +156,7 @@ static int decode_cidr(char *in_ipaddr_str, struct iface_rec_decode *ird) + tmp = ipaddr_str; + tok = strsep(&tmp, "/"); + LOG_INFO(PFX "in cidr: bitmask '%s' ip '%s'", tmp, tok); +- keepbits = atoi(tmp); +- strcpy(ipaddr_str, tok); ++ keepbits = strtoull(tmp, NULL, 10); + } + + /* Determine if the IP address passed from the iface file is +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-20-Check-iscsiuio-ping-data-length-for-validity.patch b/SOURCES/open-iscsi-2.0.875-20-Check-iscsiuio-ping-data-length-for-validity.patch new file mode 100644 index 0000000..b07fd86 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-20-Check-iscsiuio-ping-data-length-for-validity.patch @@ -0,0 +1,58 @@ +From 59ede2cf4eee8729a4221000a5d1ecdd312a31ac Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Fri, 15 Dec 2017 11:21:15 -0800 +Subject: [PATCH] Check iscsiuio ping data length for validity + +We do not trust that the received ping packet data length +is correct, so sanity check it. Found by Qualsys. +--- + iscsiuio/src/unix/iscsid_ipc.c | 5 +++++ + iscsiuio/src/unix/packet.c | 2 +- + iscsiuio/src/unix/packet.h | 2 ++ + 3 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/iscsiuio/src/unix/iscsid_ipc.c b/iscsiuio/src/unix/iscsid_ipc.c +index 4e3d065667c9..d4322350fcf6 100644 +--- a/iscsiuio/src/unix/iscsid_ipc.c ++++ b/iscsiuio/src/unix/iscsid_ipc.c +@@ -328,6 +328,11 @@ static void *perform_ping(void *arg) + + data = (iscsid_uip_broadcast_t *)png_c->data; + datalen = data->u.ping_rec.datalen; ++ if ((datalen > STD_MTU_SIZE) || (datalen < 0)) { ++ LOG_ERR(PFX "Ping datalen invalid: %d", datalen); ++ rc = -EINVAL; ++ goto ping_done; ++ } + + memset(dst_addr, 0, sizeof(uip_ip6addr_t)); + if (nic_iface->protocol == AF_INET) { +diff --git a/iscsiuio/src/unix/packet.c b/iscsiuio/src/unix/packet.c +index ecea09bedc22..3ce2c6b623e0 100644 +--- a/iscsiuio/src/unix/packet.c ++++ b/iscsiuio/src/unix/packet.c +@@ -112,7 +112,7 @@ int alloc_free_queue(nic_t *nic, size_t num_of_packets) + for (i = 0; i < num_of_packets; i++) { + packet_t *pkt; + +- pkt = alloc_packet(1500, 1500); ++ pkt = alloc_packet(STD_MTU_SIZE, STD_MTU_SIZE); + if (pkt == NULL) { + goto done; + } +diff --git a/iscsiuio/src/unix/packet.h b/iscsiuio/src/unix/packet.h +index b63d68851bb4..19d1db912d18 100644 +--- a/iscsiuio/src/unix/packet.h ++++ b/iscsiuio/src/unix/packet.h +@@ -43,6 +43,8 @@ + + #include "nic.h" + ++#define STD_MTU_SIZE 1500 ++ + struct nic; + struct nic_interface; + +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-21-tell-git-to-ignore-the-iscsiuio-binary.patch b/SOURCES/open-iscsi-2.0.875-21-tell-git-to-ignore-the-iscsiuio-binary.patch new file mode 100644 index 0000000..843e15c --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-21-tell-git-to-ignore-the-iscsiuio-binary.patch @@ -0,0 +1,20 @@ +From 8513665ee05388ec379ac55ed2e17cd634d04474 Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Fri, 15 Dec 2017 11:23:38 -0800 +Subject: [PATCH] tell git to ignore the iscsiuio binary + +--- + iscsiuio/src/unix/.gitignore | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/iscsiuio/src/unix/.gitignore b/iscsiuio/src/unix/.gitignore +index a2dca2d5700b..b3b37db48049 100644 +--- a/iscsiuio/src/unix/.gitignore ++++ b/iscsiuio/src/unix/.gitignore +@@ -1,2 +1,3 @@ + build_date.c + build_date.h ++iscsiuio +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-30-Cleanup-iscsiuio-master-Makefile-template.patch b/SOURCES/open-iscsi-2.0.875-30-Cleanup-iscsiuio-master-Makefile-template.patch new file mode 100644 index 0000000..0ad160b --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-30-Cleanup-iscsiuio-master-Makefile-template.patch @@ -0,0 +1,39 @@ +From 16057538f6ff0209a0dfe40067d6fc7a8cadeaab Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Wed, 17 Jan 2018 14:17:13 -0800 +Subject: [PATCH] Cleanup iscsiuio master Makefile template. + +Make sure needed directories exist, and make +sure install/symbolic linking of target +works, even when not in root=/. +--- + iscsiuio/Makefile.am | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/iscsiuio/Makefile.am b/iscsiuio/Makefile.am +index c70103027be5..97f478fa2fe6 100644 +--- a/iscsiuio/Makefile.am ++++ b/iscsiuio/Makefile.am +@@ -19,11 +19,15 @@ install-am: all-am + + install-man: + cat docs/iscsiuio.8 | GZIP=$(GZIP_ENV) gzip -c > iscsiuio.8.gz +- $(INSTALL_PROGRAM) iscsiuio.8.gz $(mandir)/man8 ++ $(INSTALL) -d $(DESTDIR)$(mandir)/man8/ ++ $(INSTALL_DATA) iscsiuio.8.gz $(DESTDIR)$(mandir)/man8/ + + install-log: +- $(INSTALL_PROGRAM) iscsiuiolog $(logdir) ++ $(INSTALL) -d $(DESTDIR)$(logdir)/ ++ $(INSTALL_DATA) iscsiuiolog $(DESTDIR)$(logdir)/ + + install-brcm: +- -rm -f $(sbindir)/brcm_iscsiuio +- -ln -s $(sbindir)/iscsiuio $(sbindir)/brcm_iscsiuio ++ $(RM) $(DESTDIR)$(sbindir)/brcm_iscsiuio ++ (cd $(DESTDIR)/$(sbindir); \ ++ $(RM) brcm_iscsiuio; \ ++ $(LN_S) iscsiuio brcm_iscsiuio) +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-7-Ignore-library-file-for-iscsiuio-src.patch b/SOURCES/open-iscsi-2.0.875-7-Ignore-library-file-for-iscsiuio-src.patch new file mode 100644 index 0000000..0d5ccf8 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-7-Ignore-library-file-for-iscsiuio-src.patch @@ -0,0 +1,20 @@ +From 204f588d8be50354ab4a3f3ace671d27d11f347d Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Sat, 2 Dec 2017 14:01:34 -0800 +Subject: [PATCH] Ignore library file for iscsiuio/src + +--- + iscsiuio/src/.gitignore | 1 + + 1 file changed, 1 insertion(+) + create mode 100644 iscsiuio/src/.gitignore + +diff --git a/iscsiuio/src/.gitignore b/iscsiuio/src/.gitignore +new file mode 100644 +index 000000000000..10301e28bf0f +--- /dev/null ++++ b/iscsiuio/src/.gitignore +@@ -0,0 +1 @@ ++*.a +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.875-9-Remove-unused-variables.-No-functional-change.patch b/SOURCES/open-iscsi-2.0.875-9-Remove-unused-variables.-No-functional-change.patch new file mode 100644 index 0000000..da6510b --- /dev/null +++ b/SOURCES/open-iscsi-2.0.875-9-Remove-unused-variables.-No-functional-change.patch @@ -0,0 +1,93 @@ +From de7f6ca8a583136d61cfb213f4c9873f91840e3b Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Sat, 2 Dec 2017 14:17:15 -0800 +Subject: [PATCH] Remove unused variables. No functional change. + +This makes the compiler much happier, and +debugging easier. +--- + iscsiuio/src/uip/uip.c | 3 --- + iscsiuio/src/unix/iscsid_ipc.c | 5 ----- + iscsiuio/src/unix/libs/bnx2.c | 1 - + iscsiuio/src/unix/libs/cnic.c | 3 --- + iscsiuio/src/unix/nic_utils.c | 2 -- + 5 files changed, 14 deletions(-) + +diff --git a/iscsiuio/src/uip/uip.c b/iscsiuio/src/uip/uip.c +index 9e6dcc3b87b9..e2ce2cc5d85c 100644 +--- a/iscsiuio/src/uip/uip.c ++++ b/iscsiuio/src/uip/uip.c +@@ -102,9 +102,6 @@ + ******************************************************************************/ + #define PFX "uip " + +-static const uip_ip6addr_t all_ones_addr6 = { +- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff +-}; + static const uip_ip4addr_t all_ones_addr4 = { 0xffff, 0xffff }; + + const uip_ip6addr_t all_zeroes_addr6 = { +diff --git a/iscsiuio/src/unix/iscsid_ipc.c b/iscsiuio/src/unix/iscsid_ipc.c +index a2a59a8a1821..658362899234 100644 +--- a/iscsiuio/src/unix/iscsid_ipc.c ++++ b/iscsiuio/src/unix/iscsid_ipc.c +@@ -103,11 +103,6 @@ struct iface_rec_decode { + }; + + +-/****************************************************************************** +- * iscsid_ipc Constants +- *****************************************************************************/ +-static const char uio_udev_path_template[] = "/dev/uio%d"; +- + /****************************************************************************** + * Globals + *****************************************************************************/ +diff --git a/iscsiuio/src/unix/libs/bnx2.c b/iscsiuio/src/unix/libs/bnx2.c +index c68131c0ca24..1181cf4ddd31 100644 +--- a/iscsiuio/src/unix/libs/bnx2.c ++++ b/iscsiuio/src/unix/libs/bnx2.c +@@ -77,7 +77,6 @@ static const char cnic_uio_sysfs_name[] = "bnx2_cnic"; + /******************************************************************************* + * String constants used to display human readable adapter name + ******************************************************************************/ +-static const char brcm_5706C[] = "QLogic NetXtreme II BCM5706 1000Base-T"; + static const char hp_NC370T[] = + "HP NC370T Multifunction Gigabit Server Adapter"; + static const char hp_NC370I[] = +diff --git a/iscsiuio/src/unix/libs/cnic.c b/iscsiuio/src/unix/libs/cnic.c +index a009f25f0814..9662f024041b 100644 +--- a/iscsiuio/src/unix/libs/cnic.c ++++ b/iscsiuio/src/unix/libs/cnic.c +@@ -73,9 +73,6 @@ + ******************************************************************************/ + #define PFX "CNIC " + +-static const uip_ip6addr_t all_ones_addr6 = { +- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; +- + /******************************************************************************* + * Constants shared between the bnx2 and bnx2x modules + ******************************************************************************/ +diff --git a/iscsiuio/src/unix/nic_utils.c b/iscsiuio/src/unix/nic_utils.c +index 6e580f862eea..e2f294389840 100644 +--- a/iscsiuio/src/unix/nic_utils.c ++++ b/iscsiuio/src/unix/nic_utils.c +@@ -76,14 +76,12 @@ static const char uio_uevent_path_template[] = "/sys/class/uio/uio%d/uevent"; + + static const char base_iscsi_host_name[] = "/sys/class/iscsi_host/"; + static const char host_template[] = "host%d"; +-static const char iscsi_host_path_template[] = "/sys/class/iscsi_host/host%d"; + 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"; + + /** +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.876-3-qedi.c-Removed-unused-linux-ethtool.h.patch b/SOURCES/open-iscsi-2.0.876-3-qedi.c-Removed-unused-linux-ethtool.h.patch new file mode 100644 index 0000000..fc76864 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-3-qedi.c-Removed-unused-linux-ethtool.h.patch @@ -0,0 +1,31 @@ +From: Chris Leech +Subject: qedi.c: Removed unused linux/ethtool.h + +Bugzilla: ZZZ +Upstream Status: +Build Info: XXX +Tested: + +commit 197713ad7e3e944102bbd792e1ab9ec4a67100c0 +Author: Khem Raj +Date: Fri Feb 2 23:25:21 2018 -0800 + + qedi.c: Removed unused linux/ethtool.h + + Signed-off-by: Khem Raj +--- + iscsiuio/src/unix/libs/qedi.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/iscsiuio/src/unix/libs/qedi.c b/iscsiuio/src/unix/libs/qedi.c +index b81fecdfe78f..24cb89ad3580 100644 +--- a/iscsiuio/src/unix/libs/qedi.c ++++ b/iscsiuio/src/unix/libs/qedi.c +@@ -49,7 +49,6 @@ + #include + #include + #include +-#include + #include + #include + #include diff --git a/SOURCES/open-iscsi-2.0.876-31-Fix-iscsiuio-segfault-when-shutting-down.patch b/SOURCES/open-iscsi-2.0.876-31-Fix-iscsiuio-segfault-when-shutting-down.patch new file mode 100644 index 0000000..2b32cd9 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-31-Fix-iscsiuio-segfault-when-shutting-down.patch @@ -0,0 +1,36 @@ +From: Chris Leech +Subject: Fix iscsiuio segfault when shutting down. + +Bugzilla: ZZZ +Upstream Status: +Build Info: XXX +Tested: + +commit 2bdcae8a9969295183377342cfe218d1c5d263db +Author: Lee Duncan +Date: Thu May 31 08:42:58 2018 -0700 + + Fix iscsiuio segfault when shutting down. + + If iscsiuio is stopped when first starting up, it can easily + hit a window where pthread_cancel() is called before pthread_create() + finishesx The problem was found by kiyo@jp.fujitsu.com, who also + proposed the fix. +--- + iscsiuio/src/unix/iscsid_ipc.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/iscsiuio/src/unix/iscsid_ipc.c b/iscsiuio/src/unix/iscsid_ipc.c +index d4322350fcf6..2acac485af72 100644 +--- a/iscsiuio/src/unix/iscsid_ipc.c ++++ b/iscsiuio/src/unix/iscsid_ipc.c +@@ -1245,7 +1245,8 @@ void iscsid_cleanup() + { + int rc; + +- if (iscsid_opts.fd != INVALID_FD) { ++ if (iscsid_opts.fd != INVALID_FD && ++ iscsid_opts.thread != INVALID_THREAD) { + rc = pthread_cancel(iscsid_opts.thread); + if (rc != 0) { + LOG_ERR("Could not cancel iscsid listening thread: %s", diff --git a/SOURCES/open-iscsi-2.0.876-41-vlan-setting-sync-across-ipv4-ipv6-for-be2iscsi.patch b/SOURCES/open-iscsi-2.0.876-41-vlan-setting-sync-across-ipv4-ipv6-for-be2iscsi.patch new file mode 100644 index 0000000..5b82a26 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-41-vlan-setting-sync-across-ipv4-ipv6-for-be2iscsi.patch @@ -0,0 +1,208 @@ +From 2cee7f4a011619480c83d8279fe5d15de6e37ea8 Mon Sep 17 00:00:00 2001 +From: Chris Leech +Date: Fri, 22 Jun 2018 12:25:05 -0700 +Subject: [PATCH 1/1] vlan setting sync across ipv4/ipv6 for be2iscsi + +be2iscsi exports two ifaces per host port for ipv4 and ipv6 network +configurations. But, they need to have the same link level configuration +including vlan settings. If vlan setting are modified in only one iface +record, then whichever record is applied last (filesystem dependant I +think) will take effect and things may not work. + +This change to iscsiadm applies vlan updates to all records with +matching MAC addresses if a flag is set on the transport. +The new transport flag is only set for be2iscsi. +--- + usr/iscsiadm.c | 132 ++++++++++++++++++++++++++++++++++++++++++++------------ + usr/transport.c | 1 + + usr/transport.h | 3 ++ + 3 files changed, 108 insertions(+), 28 deletions(-) + +diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c +index 6245e89d46cb..9083132942e2 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(¶m->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, +@@ -2320,6 +2403,8 @@ static int exec_iface_op(int op, int do_show, int info_level, + struct node_rec *rec = NULL; + int rc = 0; + ++ LIST_HEAD(vlan_params); ++ struct iscsi_transport *t; + switch (op) { + case OP_NEW: + if (!iface) { +@@ -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); +-- +2.14.4 + diff --git a/SOURCES/open-iscsi-2.0.876-5-bnx2x.c-Reorder-the-includes-to-avoid-duplicate-defines-with-musl.patch b/SOURCES/open-iscsi-2.0.876-5-bnx2x.c-Reorder-the-includes-to-avoid-duplicate-defines-with-musl.patch new file mode 100644 index 0000000..faf5a3f --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-5-bnx2x.c-Reorder-the-includes-to-avoid-duplicate-defines-with-musl.patch @@ -0,0 +1,53 @@ +From 96a2c0c1629f05d452af32c809f30bc4d0c3220e Mon Sep 17 00:00:00 2001 +From: Khem Raj +Date: Fri, 2 Feb 2018 23:28:33 -0800 +Subject: [PATCH] bnx2x.c: Reorder the includes to avoid duplicate defines with + musl + +including nic.h before linux/ethtool.h avoids redefinitions of +eth structs + +/mnt/a/oe/build/tmp/work/cortexa7t2hf-neon-vfpv4-bec-linux-musleabi/iscsi-initiator-utils/2.0.876-r0/recipe-sysroot/ +usr/include/netinet/if_ether.h:104:8: error: redefinition of 'struct ethhdr' + struct ethhdr { + ^~~~~~ +In file included from /mnt/a/oe/build/tmp/work/cortexa7t2hf-neon-vfpv4-bec-linux-musleabi/iscsi-initiator-utils/2.0. +876-r0/recipe-sysroot/usr/include/linux/ethtool.h:19:0, + from qedi.c:52: +/mnt/a/oe/build/tmp/work/cortexa7t2hf-neon-vfpv4-bec-linux-musleabi/iscsi-initiator-utils/2.0.876-r0/recipe-sysroot/ +usr/include/linux/if_ether.h:154:8: note: originally defined here + struct ethhdr { + ^~~~~~ + +Signed-off-by: Khem Raj +--- + iscsiuio/src/unix/libs/bnx2x.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/iscsiuio/src/unix/libs/bnx2x.c b/iscsiuio/src/unix/libs/bnx2x.c +index 3df6d5fdbfa2..c5e7b7191811 100644 +--- a/iscsiuio/src/unix/libs/bnx2x.c ++++ b/iscsiuio/src/unix/libs/bnx2x.c +@@ -36,6 +36,11 @@ + * bnx2x.c - bnx2x user space driver + * + */ ++ ++/* include nic.h before linux/ethtool.h to avoid redefinitions of ++ * eth structs ++*/ ++#include "nic.h" + #include + #include + #include +@@ -58,7 +63,6 @@ + #include "bnx2x.h" + #include "cnic.h" + #include "logger.h" +-#include "nic.h" + #include "nic_id.h" + #include "nic_utils.h" + #include "options.h" +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.876-54-iscsiuio-Add-inter-host-mutex-while-doing-xmit.patch b/SOURCES/open-iscsi-2.0.876-54-iscsiuio-Add-inter-host-mutex-while-doing-xmit.patch new file mode 100644 index 0000000..98b6f70 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-54-iscsiuio-Add-inter-host-mutex-while-doing-xmit.patch @@ -0,0 +1,248 @@ +From d469a2e15e8558cf8ba34db376b4b777177a016b Mon Sep 17 00:00:00 2001 +From: Chris Leech +Date: Fri, 22 Jun 2018 12:30:09 -0700 +Subject: [PATCH 1/1] iscsiuio: Add inter-host mutex while doing xmit + +Bugzilla: ZZZ +Upstream Status: +Build Info: XXX +Tested: + +commit 9501dcaf5be474b4cb882a5e00f875f8c57e0e8e +Author: Manish Rangankar +Date: Wed Jun 20 02:33:36 2018 -0400 + + iscsiuio: Add inter-host mutex while doing xmit + + This avoids the netlink buffer corruption when more than one host + try to xmit packet at the same time. + + Signed-off-by: Manish Rangankar +--- + iscsiuio/src/unix/libs/cnic.c | 2 ++ + iscsiuio/src/unix/libs/qedi.c | 38 +++++++++++++++++++++++++++++++++----- + iscsiuio/src/unix/nic.c | 15 ++++++++++++--- + iscsiuio/src/unix/nic_utils.c | 4 ++++ + 4 files changed, 51 insertions(+), 8 deletions(-) + +diff --git a/iscsiuio/src/unix/libs/cnic.c b/iscsiuio/src/unix/libs/cnic.c +index 32166edf243f..b953278e5eab 100644 +--- a/iscsiuio/src/unix/libs/cnic.c ++++ b/iscsiuio/src/unix/libs/cnic.c +@@ -106,6 +106,8 @@ static int cnic_arp_send(nic_t *nic, nic_interface_t *nic_iface, int fd, + static const uint8_t multicast_mac[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + ++ LOG_DEBUG(PFX "%s: host:%d - try getting xmit mutex cnic arp send", ++ nic->log_name, nic->host_no); + rc = pthread_mutex_trylock(&nic->xmit_mutex); + if (rc != 0) { + LOG_DEBUG(PFX "%s: could not get xmit_mutex", nic->log_name); +diff --git a/iscsiuio/src/unix/libs/qedi.c b/iscsiuio/src/unix/libs/qedi.c +index 5bb0682c8bed..32cab48fed17 100644 +--- a/iscsiuio/src/unix/libs/qedi.c ++++ b/iscsiuio/src/unix/libs/qedi.c +@@ -74,6 +74,8 @@ + + extern int nl_sock; + ++static pthread_mutex_t host_mutex = PTHREAD_MUTEX_INITIALIZER; ++ + /* Foward struct declarations */ + struct nic_ops qedi_op; + +@@ -812,15 +814,26 @@ static void qedi_prepare_xmit_packet(nic_t *nic, + 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; + ++ LOG_DEBUG(PFX "%s: pkt->buf_size=%d tpid=0x%x", nic->log_name, ++ pkt->buf_size, eth_vlan->tpid); ++ + 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)); ++ ++ LOG_DEBUG(PFX "%s: pkt->buf_size=%d type=0x%x", nic->log_name, ++ pkt->buf_size, eth->type); ++ LOG_DEBUG(PFX "%s: pkt->buf_size - eth_hdr_size = %d", nic->log_name, ++ pkt->buf_size - 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 { ++ LOG_DEBUG(PFX "%s: NO VLAN pkt->buf_size=%d", nic->log_name, ++ pkt->buf_size); + memcpy(bp->tx_pkt, pkt->buf, pkt->buf_size); + } + +@@ -876,11 +889,18 @@ void qedi_start_xmit(nic_t *nic, size_t len, u16_t vlan_id) + path_data->handle = QEDI_PATH_HANDLE; + path_data->vlan_id = vlan_id; + uctrl->host_tx_pkt_len = len; ++ LOG_DEBUG(PFX "%s: host_no:%d vlan_id=%d, tx_pkt_len=%d", ++ nic->log_name, ev->u.set_path.host_no, path_data->vlan_id, uctrl->host_tx_pkt_len); + ++ LOG_DEBUG(PFX "%s: ACQUIRE HOST MUTEX", nic->log_name); ++ pthread_mutex_lock(&host_mutex); + rc = __kipc_call(nl_sock, ev, buflen); + if (rc > 0) { + bp->tx_prod++; + uctrl->host_tx_prod++; ++ LOG_DEBUG(PFX "%s: bp->tx_prod: %d, uctrl->host_tx_prod=%d", ++ nic->log_name, 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); +@@ -888,6 +908,8 @@ void qedi_start_xmit(nic_t *nic, size_t len, u16_t vlan_id) + LOG_ERR(PFX "Pkt transmission failed: %d", rc); + } + ++ LOG_DEBUG(PFX "%s: RELEASE HOST MUTEX", nic->log_name); ++ pthread_mutex_unlock(&host_mutex); + free(ubuf); + } + +@@ -923,14 +945,18 @@ int qedi_write(nic_t *nic, nic_interface_t *nic_iface, packet_t *pkt) + struct timespec sleep_req = {.tv_sec = 0, .tv_nsec = 5000000 }, + sleep_rem; + ++ LOG_DEBUG(PFX "%s: host:%d - calling clear_tx_intr from qedi_write", ++ nic->log_name, nic->host_no); + if (qedi_clear_tx_intr(nic) == 0) + break; + + nanosleep(&sleep_req, &sleep_rem); + } + ++ LOG_DEBUG(PFX "%s: host:%d - try getting xmit mutex", ++ nic->log_name, nic->host_no); + if (pthread_mutex_trylock(&nic->xmit_mutex) != 0) { +- LOG_PACKET(PFX "%s: Dropped previous transmitted packet", ++ LOG_DEBUG(PFX "%s: Dropped previous transmitted packet", + nic->log_name); + return -EINVAL; + } +@@ -944,7 +970,7 @@ int qedi_write(nic_t *nic, nic_interface_t *nic_iface, packet_t *pkt) + 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", ++ LOG_DEBUG(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); + +@@ -1071,7 +1097,7 @@ static int qedi_clear_tx_intr(nic_t *nic) + return -EINVAL; + } + +- LOG_PACKET(PFX "%s: clearing tx interrupt [%d %d]", ++ LOG_DEBUG(PFX "%s: clearing tx interrupt [%d %d]", + nic->log_name, bp->tx_cons, hw_cons); + bp->tx_cons = hw_cons; + +@@ -1083,7 +1109,7 @@ static int qedi_clear_tx_intr(nic_t *nic) + packet_t *pkt; + int i; + +- LOG_PACKET(PFX "%s: sending queued tx packet", nic->log_name); ++ LOG_DEBUG(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 +@@ -1096,7 +1122,7 @@ static int qedi_clear_tx_intr(nic_t *nic) + (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", ++ LOG_DEBUG(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); + +@@ -1124,6 +1150,8 @@ static int qedi_clear_tx_intr(nic_t *nic) + } + } + ++ LOG_DEBUG(PFX "%s: host:%d - releasing xmit mutex", ++ nic->log_name, nic->host_no); + pthread_mutex_unlock(&nic->xmit_mutex); + + return 0; +diff --git a/iscsiuio/src/unix/nic.c b/iscsiuio/src/unix/nic.c +index a5f714a91923..dfc2ad0a7a7b 100644 +--- a/iscsiuio/src/unix/nic.c ++++ b/iscsiuio/src/unix/nic.c +@@ -799,6 +799,8 @@ int nic_process_intr(nic_t *nic, int discard_check) + + nic->intr_count = count; + ++ LOG_DEBUG(PFX "%s: host:%d - calling clear_tx_intr from process_intr", ++ nic->log_name, nic->host_no); + (*nic->ops->clear_tx_intr) (nic); + ret = 1; + } +@@ -1036,9 +1038,11 @@ int process_packets(nic_t *nic, + case UIP_ETHTYPE_IPv4: + case UIP_ETHTYPE_ARP: + af_type = AF_INET; ++ LOG_DEBUG(PFX "%s: ARP or IPv4 vlan:0x%x ethertype:0x%x", ++ nic->log_name, vlan_id, type); + break; + default: +- LOG_PACKET(PFX "%s: Ignoring vlan:0x%x ethertype:0x%x", ++ LOG_DEBUG(PFX "%s: Ignoring vlan:0x%x ethertype:0x%x", + nic->log_name, vlan_id, type); + goto done; + } +@@ -1064,7 +1068,7 @@ int process_packets(nic_t *nic, + if (nic_iface == NULL) { + /* Matching nic_iface not found */ + pthread_mutex_unlock(&nic->nic_mutex); +- LOG_PACKET(PFX "%s: Couldn't find interface for " ++ LOG_DEBUG(PFX "%s: Couldn't find interface for " + "VLAN: %d af_type %d", + nic->log_name, vlan_id, af_type); + rc = EINVAL; /* Return the +error code */ +@@ -1118,6 +1122,8 @@ nic_iface_present: + prepare_ipv4_packet(nic, nic_iface, + ustack, pkt); + ++ LOG_DEBUG(PFX "%s: write called after arp_ipin, uip_len=%d", ++ nic->log_name, ustack->uip_len); + (*nic->ops->write) (nic, nic_iface, pkt); + } + +@@ -1129,8 +1135,11 @@ nic_iface_present: + * in data that should be sent out on the + * network, the global variable uip_len + * is set to a value > 0. */ +- if (pkt->buf_size > 0) ++ if (pkt->buf_size > 0) { ++ LOG_DEBUG(PFX "%s: write called after arp_arpin, bufsize=%d", ++ nic->log_name, pkt->buf_size); + (*nic->ops->write) (nic, nic_iface, pkt); ++ } + break; + } + ustack->uip_len = 0; +diff --git a/iscsiuio/src/unix/nic_utils.c b/iscsiuio/src/unix/nic_utils.c +index 6e580f862eea..988905bca2a9 100644 +--- a/iscsiuio/src/unix/nic_utils.c ++++ b/iscsiuio/src/unix/nic_utils.c +@@ -1435,6 +1435,10 @@ nic_interface_t *nic_find_nic_iface(nic_t *nic, + nic_interface_t *current_vlan = NULL; + + while (current != NULL) { ++ LOG_DEBUG(PFX "%s: incoming protocol: %d, vlan_id:%d iface_num: %d, request_type: %d", ++ nic->log_name, protocol, vlan_id, iface_num, request_type); ++ LOG_DEBUG(PFX "%s: host:%d iface_num: 0x%x VLAN: %d protocol: %d", ++ nic->log_name, nic->host_no, current->iface_num, current->vlan_id, current->protocol); + if (current->protocol != protocol) + goto next; + +-- +2.14.4 + diff --git a/SOURCES/open-iscsi-2.0.876-56-iscsiuio-Release-xmit_mutex-in-error-code-path.patch b/SOURCES/open-iscsi-2.0.876-56-iscsiuio-Release-xmit_mutex-in-error-code-path.patch new file mode 100644 index 0000000..d46b3e7 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-56-iscsiuio-Release-xmit_mutex-in-error-code-path.patch @@ -0,0 +1,28 @@ +From cda251fc8bcf57454a2573d5cd097666a88efe72 Mon Sep 17 00:00:00 2001 +From: Manish Rangankar +Date: Tue, 14 Aug 2018 02:15:20 -0400 +Subject: [PATCH] iscsiuio: Release xmit_mutex in error code path. + +This prevents iscsiuio seg fault in case get_tx_pkt fails +while sending ARP. + +Signed-off-by: Manish Rangankar +--- + iscsiuio/src/unix/libs/cnic.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/iscsiuio/src/unix/libs/cnic.c b/iscsiuio/src/unix/libs/cnic.c +index 4ff61e7ad596..9cdf933f1f23 100644 +--- a/iscsiuio/src/unix/libs/cnic.c ++++ b/iscsiuio/src/unix/libs/cnic.c +@@ -114,6 +114,7 @@ static int cnic_arp_send(nic_t *nic, nic_interface_t *nic_iface, int fd, + eth = (*nic->ops->get_tx_pkt) (nic); + if (eth == NULL) { + LOG_WARN(PFX "%s: couldn't get tx packet", nic->log_name); ++ pthread_mutex_unlock(&nic->xmit_mutex); + return -EAGAIN; + } + +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.876-57-iscsiuio-limit-retries-of-performing-dhcpv6-before-declaring-dhcp-failure.patch b/SOURCES/open-iscsi-2.0.876-57-iscsiuio-limit-retries-of-performing-dhcpv6-before-declaring-dhcp-failure.patch new file mode 100644 index 0000000..374c2d1 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-57-iscsiuio-limit-retries-of-performing-dhcpv6-before-declaring-dhcp-failure.patch @@ -0,0 +1,47 @@ +From 65bcce29d5d7ad3831513fe14c76791287847597 Mon Sep 17 00:00:00 2001 +From: Nilesh Javali +Date: Fri, 24 Aug 2018 06:38:45 -0400 +Subject: [PATCH] iscsiuio: limit retries of performing dhcpv6 before declaring + dhcp failure + +After successful stateless router adv., the restart of handle_ndp thread +infinitely checks dhcpv6 causing IPv6 DHCP sequence failure. +Limit the retries of dhcpv6 validation. + +Signed-off-by: Nilesh Javali +--- + iscsiuio/src/uip/ipv6_ndpc.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/iscsiuio/src/uip/ipv6_ndpc.c b/iscsiuio/src/uip/ipv6_ndpc.c +index a396cb784bd3..bb07c1dd64ff 100644 +--- a/iscsiuio/src/uip/ipv6_ndpc.c ++++ b/iscsiuio/src/uip/ipv6_ndpc.c +@@ -134,6 +134,7 @@ wait_rtr: + } while (!(ipv6c->flags & IPV6_FLAGS_ROUTER_ADV_RECEIVED)); + + LOG_DEBUG("%s: ndpc_handle got rtr adv", s->nic->log_name); ++ s->retry_count = 0; + + no_rtr_adv: + s->state = NDPC_STATE_RTR_ADV; +@@ -200,9 +201,13 @@ wait_dhcp: + } else { + /* Static IPv6 */ + if (ustack->ip_config == IPV6_CONFIG_DHCP) { +- LOG_DEBUG("%s: ndpc_handle DHCP failed", +- s->nic->log_name); +- PT_RESTART(&s->pt); ++ s->retry_count++; ++ if (s->retry_count > DHCPV6_NUM_OF_RETRY) { ++ LOG_DEBUG("%s: ndpc_handle DHCP failed", ++ s->nic->log_name); ++ } else { ++ PT_RESTART(&s->pt); ++ } + } + staticv6: + ipv6_disable_dhcpv6(ipv6c); +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.876-58-iscsiuio-Do-not-flush-tx-queue-on-each-uio-interrupt.patch b/SOURCES/open-iscsi-2.0.876-58-iscsiuio-Do-not-flush-tx-queue-on-each-uio-interrupt.patch new file mode 100644 index 0000000..f7e90c8 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-58-iscsiuio-Do-not-flush-tx-queue-on-each-uio-interrupt.patch @@ -0,0 +1,37 @@ +From caae91b5ef27124caf9adc1b46b40a3491f49bee Mon Sep 17 00:00:00 2001 +From: Manish Rangankar +Date: Wed, 24 Oct 2018 07:22:05 -0400 +Subject: [PATCH] iscsiuio: Do not flush tx queue on each uio interrupt. + +Unlike bnx2x, qedi start_xmit netlink provide us guarantee +of transmitting LL2 packet, so there is no need to call clear_tx_intr for +each LL2 packet. This help us in reducing iscsiuio lock contention. + +Signed-off-by: Manish Rangankar +--- + iscsiuio/src/unix/nic.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/iscsiuio/src/unix/nic.c b/iscsiuio/src/unix/nic.c +index dfc2ad0a7a7b..29c4332a3162 100644 +--- a/iscsiuio/src/unix/nic.c ++++ b/iscsiuio/src/unix/nic.c +@@ -799,9 +799,12 @@ int nic_process_intr(nic_t *nic, int discard_check) + + nic->intr_count = count; + +- LOG_DEBUG(PFX "%s: host:%d - calling clear_tx_intr from process_intr", +- nic->log_name, nic->host_no); +- (*nic->ops->clear_tx_intr) (nic); ++ if (strcmp(nic->ops->description, "qedi")) { ++ LOG_DEBUG(PFX "%s: host:%d - calling clear_tx_intr from process_intr", ++ nic->log_name, nic->host_no); ++ (*nic->ops->clear_tx_intr) (nic); ++ } ++ + ret = 1; + } + pthread_mutex_unlock(&nic->nic_mutex); +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.876-59-qedi-Set-buf_size-in-case-of-ICMP-and-ARP-packet.patch b/SOURCES/open-iscsi-2.0.876-59-qedi-Set-buf_size-in-case-of-ICMP-and-ARP-packet.patch new file mode 100644 index 0000000..e858e86 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-59-qedi-Set-buf_size-in-case-of-ICMP-and-ARP-packet.patch @@ -0,0 +1,35 @@ +From 243332dfaa6ba856f81c8264a3d861fe7a05eb66 Mon Sep 17 00:00:00 2001 +From: Manish Rangankar +Date: Tue, 20 Nov 2018 00:11:16 -0500 +Subject: [PATCH] qedi: Set buf_size in case of ICMP and ARP packet. + +Invalid ether len was resulting into showing CRC error on wire. + +Signed-off-by: Manish Rangankar +--- + iscsiuio/src/unix/nic.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/iscsiuio/src/unix/nic.c b/iscsiuio/src/unix/nic.c +index 29c4332a3162..bd415c2446bc 100644 +--- a/iscsiuio/src/unix/nic.c ++++ b/iscsiuio/src/unix/nic.c +@@ -1122,6 +1122,7 @@ nic_iface_present: + * network, the global variable uip_len is + * set to a value > 0. */ + if (ustack->uip_len > 0) { ++ pkt->buf_size = ustack->uip_len; + prepare_ipv4_packet(nic, nic_iface, + ustack, pkt); + +@@ -1139,6 +1140,7 @@ nic_iface_present: + * network, the global variable uip_len + * is set to a value > 0. */ + if (pkt->buf_size > 0) { ++ pkt->buf_size = ustack->uip_len; + LOG_DEBUG(PFX "%s: write called after arp_arpin, bufsize=%d", + nic->log_name, pkt->buf_size); + (*nic->ops->write) (nic, nic_iface, pkt); +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.876-6-Use-correct-size-when-copying-nic-name.patch b/SOURCES/open-iscsi-2.0.876-6-Use-correct-size-when-copying-nic-name.patch new file mode 100644 index 0000000..0b52463 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-6-Use-correct-size-when-copying-nic-name.patch @@ -0,0 +1,35 @@ +From 7fd4eb9313154f3b3458ea07d6a12f0be26d2a4e Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Wed, 21 Mar 2018 16:43:33 -0700 +Subject: [PATCH] Use correct size when copying nic name. + +The incorrect length was being used to copy +the NIC name. + +Found by gcc-8, which gave this error: +> [ 19s] nic_utils.c: In function 'nic_util_enable_disable_multicast': +> [ 19s] nic_utils.c:308:9: warning: argument to 'sizeof' in 'strncpy' +> call is the same expression as the source; did you mean to +> use the size of the destination? [-Wsizeof-pointer-memaccess] +> [ 19s] sizeof(nic->eth_device_name)); +> [ 19s] ^ +--- + iscsiuio/src/unix/nic_utils.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/iscsiuio/src/unix/nic_utils.c b/iscsiuio/src/unix/nic_utils.c +index e2f294389840..786f2a4d5b5d 100644 +--- a/iscsiuio/src/unix/nic_utils.c ++++ b/iscsiuio/src/unix/nic_utils.c +@@ -305,7 +305,7 @@ static int nic_util_enable_disable_multicast(nic_t *nic, uint32_t cmd) + /* Prepare the request */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, nic->eth_device_name, +- sizeof(nic->eth_device_name)); ++ sizeof(ifr.ifr_name)); + memcpy(ifr.ifr_hwaddr.sa_data, multicast_addr.addr, ETH_ALEN); + + fd = socket(AF_INET, SOCK_DGRAM, 0); +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.876-60-qedi-Use-uio-BD-index-instead-on-buffer-index.patch b/SOURCES/open-iscsi-2.0.876-60-qedi-Use-uio-BD-index-instead-on-buffer-index.patch new file mode 100644 index 0000000..7cf2154 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-60-qedi-Use-uio-BD-index-instead-on-buffer-index.patch @@ -0,0 +1,57 @@ +From e34658ca383a0d1d2e455a4c5bb55ce06790ba97 Mon Sep 17 00:00:00 2001 +From: Manish Rangankar +Date: Tue, 20 Nov 2018 00:15:01 -0500 +Subject: [PATCH] qedi: Use uio BD index instead on buffer index. + +1. Use HW BD prod index to read packet buffer descriptor. +2. Use BD producer and consumer index to check for new data instead + of UIO ring buffer index. + +NOTE - This patch has dependency on upstream qedi driver v8.33.0.21, +commit "qedi: Move LL2 producer index processing in BH.", +(https://marc.info/?l=linux-scsi&m=154271028608808) + +Signed-off-by: Manish Rangankar +--- + iscsiuio/src/unix/libs/qedi.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/iscsiuio/src/unix/libs/qedi.c b/iscsiuio/src/unix/libs/qedi.c +index 3aa2de7155e8..b7595d5223eb 100644 +--- a/iscsiuio/src/unix/libs/qedi.c ++++ b/iscsiuio/src/unix/libs/qedi.c +@@ -994,7 +994,7 @@ static int qedi_read(nic_t *nic, packet_t *pkt) + void *rx_pkt; + int rc = 0; + uint32_t sw_cons, bd_cons; +- uint32_t hw_prod; ++ uint32_t hw_prod, bd_prod; + uint32_t rx_pkt_idx; + int len; + struct qedi_rx_bd *rx_bd; +@@ -1013,14 +1013,20 @@ static int qedi_read(nic_t *nic, packet_t *pkt) + msync(bp->rx_comp_ring, nic->page_size, MS_SYNC); + uctrl = (struct qedi_uio_ctrl *)bp->uctrl_map; + hw_prod = uctrl->hw_rx_prod; ++ bd_prod = uctrl->hw_rx_bd_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)); ++ rx_bd = bp->rx_comp_ring + (bd_prod * 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:hw_prod %d bd_prod %d, rx_pkt_idx %d, rxlen %d", ++ nic->log_name, hw_prod, bd_prod, rx_bd->rx_pkt_index, len); ++ LOG_DEBUG(PFX "%s: sw_con %d bd_cons %d num BD %d", ++ nic->log_name, sw_cons, bd_cons, QEDI_NUM_RX_BD); ++ ++ if (bd_cons != bd_prod) { + LOG_DEBUG(PFX "%s: clearing rx interrupt: %d %d", + nic->log_name, sw_cons, hw_prod); + rc = 1; +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.876-61-iscsiuio-v0.7.8.5.patch b/SOURCES/open-iscsi-2.0.876-61-iscsiuio-v0.7.8.5.patch new file mode 100644 index 0000000..bb17e70 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-61-iscsiuio-v0.7.8.5.patch @@ -0,0 +1,88 @@ +From de6e8918c6c195360d87a96c196cccef2c082527 Mon Sep 17 00:00:00 2001 +From: Nilesh Javali +Date: Tue, 20 Nov 2018 01:10:57 -0500 +Subject: [PATCH] iscsiuio: v0.7.8.5 + +Signed-off-by: Nilesh Javali +--- + iscsiuio/README | 4 ++-- + iscsiuio/RELEASE.TXT | 26 ++++++++++++++++++++++++-- + iscsiuio/configure.ac | 4 ++-- + 3 files changed, 28 insertions(+), 6 deletions(-) + +diff --git a/iscsiuio/README b/iscsiuio/README +index 9cbf7ce6717a..19150055a3fb 100644 +--- a/iscsiuio/README ++++ b/iscsiuio/README +@@ -1,6 +1,6 @@ + Iscsiuio Userspace Tool +-Version 0.7.8.4 +-Feb 22, 2018 ++Version 0.7.8.5 ++Nov 20, 2018 + ------------------------------------------------------ + + 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 ca51b26338a1..0ebf27f9539c 100644 +--- a/iscsiuio/RELEASE.TXT ++++ b/iscsiuio/RELEASE.TXT +@@ -1,7 +1,7 @@ + Release Notes + QLogic uIP Linux Driver +- Version 0.7.8.4 +- 2/22/2018 ++ Version 0.7.8.5 ++ 11/20/2018 + + QLogic Corporation + 26650 Aliso Viejo Pkwy, +@@ -11,7 +11,29 @@ + Copyright (c) 2014, QLogic Corporation + All rights reserved + ++uIP v0.7.8.5 (Nov 20, 2018) ++======================================================= ++ Fixes: ++ ------- ++ 1. Problem: CQ102578: observing ISCSI initiator IP ping drop ++ Change: 1. Do not flush tx queue on each uio interrupt ++ 2. Use UIO BD index instead on buffer index. ++ 3. Set buf_size in case of ICMP and ARP packet ++ Impact: QL41xxx adapters + ++ 2. Problem: CQ103034 - Unable to boot iSCSI BFS in IPv6 DHCP config ++ Change: Limit retries of performing dhcpv6 before declaring dhcp failure ++ Impact: All ++ ++ 3. Problem: CQ102438: I/O fails to resume on multipath LUN during port toggle. ++ Change: lib/cnic, lib/qedi, Release xmit_mutex in error code path and ++ during clear tx queue. ++ Impact: QL84xx adapters ++ ++ 4. Problem: Netlink buffer corruption when more than one host ++ try to xmit packet at the same time ++ Change: Add inter-host mutex while doing xmit ++ Impact: All + + uIP v0.7.8.4 (Feb 22, 2018) + ======================================================= +diff --git a/iscsiuio/configure.ac b/iscsiuio/configure.ac +index fa67ea74c47a..ae23078aa0a3 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.4 ++VERSION=0.7.8.5 + +-AC_INIT([iscsiuio], [0.7.8.4], [QLogic-Storage-Upstream@cavium.com]) ++AC_INIT([iscsiuio], [0.7.8.5], [QLogic-Storage-Upstream@cavium.com]) + + AM_INIT_AUTOMAKE + AC_CONFIG_HEADER(config.h) +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.876-66-Close-file-handles-when-writing-pid-files.patch b/SOURCES/open-iscsi-2.0.876-66-Close-file-handles-when-writing-pid-files.patch new file mode 100644 index 0000000..8750872 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-66-Close-file-handles-when-writing-pid-files.patch @@ -0,0 +1,44 @@ +From 38952aa277df116da0b7ef389440de34a3e61cd0 Mon Sep 17 00:00:00 2001 +From: Scott Moser +Date: Wed, 19 Sep 2018 17:21:48 -0400 +Subject: [PATCH] Close file handles when writing pid files. + +The file descriptors used for writing to the pid file were +not being closed. Just be more clean and close them. + +Also fix a debug message that said 'lock file' rather than 'pid file'. +--- + iscsiuio/src/unix/main.c | 3 ++- + usr/iscsid.c | 1 + + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/iscsiuio/src/unix/main.c b/iscsiuio/src/unix/main.c +index 5cccc76ed220..457ebe340b7d 100644 +--- a/iscsiuio/src/unix/main.c ++++ b/iscsiuio/src/unix/main.c +@@ -346,8 +346,9 @@ int main(int argc, char *argv[]) + sprintf(buf, "%d\n", getpid()); + written_bytes = write(fd, buf, strlen(buf)); + if (written_bytes == -1) +- printf("Could not write lock file [%s]", ++ printf("Could not write pid file [%s]", + strerror(errno)); ++ close(fd); + + daemon_init(); + } +diff --git a/usr/iscsid.c b/usr/iscsid.c +index b95917df3bc5..7d6032252d00 100644 +--- a/usr/iscsid.c ++++ b/usr/iscsid.c +@@ -467,6 +467,7 @@ int main(int argc, char *argv[]) + exit(ISCSI_ERR); + } + } ++ close(fd); + + daemon_init(); + } else { +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.876-66-iscsiuio-avoid-loosing-bad-rc-in-nic_nl_open.patch b/SOURCES/open-iscsi-2.0.876-66-iscsiuio-avoid-loosing-bad-rc-in-nic_nl_open.patch new file mode 100644 index 0000000..702e506 --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-66-iscsiuio-avoid-loosing-bad-rc-in-nic_nl_open.patch @@ -0,0 +1,41 @@ +From 67d24cb06da6f9bc14dc5d9b833fad0e9ca1982f Mon Sep 17 00:00:00 2001 +From: Christian Ehrhardt +Date: Thu, 20 Sep 2018 07:34:30 +0200 +Subject: [PATCH] iscsiuio: avoid loosing bad rc in nic_nl_open + +nic_nl_open might have collected a non-zero rc and jumped to the error +label but still returned zero which would indicate success. + +The function should return whatever it has collected in rc and while +working without, also explicitly initialize it to zero. + +Fixes #125 + +Signed-off-by: Christian Ehrhardt +--- + iscsiuio/src/unix/nic_nl.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/iscsiuio/src/unix/nic_nl.c b/iscsiuio/src/unix/nic_nl.c +index 1a920c7b63fe..658843236da8 100644 +--- a/iscsiuio/src/unix/nic_nl.c ++++ b/iscsiuio/src/unix/nic_nl.c +@@ -534,7 +534,7 @@ static void flush_nic_nl_process_ring(nic_t *nic) + */ + int nic_nl_open() + { +- int rc; ++ int rc = 0; + char *msg_type_str; + + /* Prepare the thread to issue the ARP's */ +@@ -676,5 +676,5 @@ int nic_nl_open() + rc = 0; + + error: +- return 0; ++ return rc; + } +-- +2.17.2 + diff --git a/SOURCES/open-iscsi-2.0.876-67-iscsiuio-fail-on-nic_nl_open-failing.patch b/SOURCES/open-iscsi-2.0.876-67-iscsiuio-fail-on-nic_nl_open-failing.patch new file mode 100644 index 0000000..39f9f4a --- /dev/null +++ b/SOURCES/open-iscsi-2.0.876-67-iscsiuio-fail-on-nic_nl_open-failing.patch @@ -0,0 +1,34 @@ +From 40c4319fd3100dcbf97880508c869978dff8f818 Mon Sep 17 00:00:00 2001 +From: Christian Ehrhardt +Date: Thu, 20 Sep 2018 07:37:01 +0200 +Subject: [PATCH] iscsiuio: fail on nic_nl_open failing + +The return value of nic_nl_open was ignored and while working by chance +being the latest call before the error label any later insertion of code +after this might break it. + +Avoid that by checking for the retval and explicitly jumping to the +error label in case it is non zero. + +Signed-off-by: Christian Ehrhardt +--- + iscsiuio/src/unix/main.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/iscsiuio/src/unix/main.c b/iscsiuio/src/unix/main.c +index 5cccc76ed220..552c96a36e86 100644 +--- a/iscsiuio/src/unix/main.c ++++ b/iscsiuio/src/unix/main.c +@@ -393,7 +393,8 @@ int main(int argc, char *argv[]) + goto error; + + /* NetLink connection to listen to NETLINK_ISCSI private messages */ +- nic_nl_open(); ++ if (nic_nl_open() != 0) ++ goto error; + + error: + cleanup(); +-- +2.17.2 + diff --git a/SPECS/iscsi-initiator-utils.spec b/SPECS/iscsi-initiator-utils.spec new file mode 100644 index 0000000..1f77c3f --- /dev/null +++ b/SPECS/iscsi-initiator-utils.spec @@ -0,0 +1,553 @@ +%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: 14%{?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 +Patch177: open-iscsi-2.0.874-30-iscsiuio-fix-dhcpv6-transaction-id-mismatch-error.patch +Patch178: open-iscsi-2.0.874-31-iscsiuio-serialize-xmit_mutex-lock-to-prevent-iscsiuio-seg-fault.patch +Patch179: open-iscsi-2.0.874-32-iscsiuio-allow-ARP-for-non-matching-src-and-dst-addresses.patch +Patch180: open-iscsi-2.0.874-33-iscsiuio-v0.7.8.4.patch +Patch181: open-iscsi-2.0.876-3-qedi.c-Removed-unused-linux-ethtool.h.patch +Patch182: open-iscsi-2.0.876-31-Fix-iscsiuio-segfault-when-shutting-down.patch +Patch183: open-iscsi-2.0.876-54-iscsiuio-Add-inter-host-mutex-while-doing-xmit.patch +Patch184: 0184-set-iscsid.safe_logout-to-Yes-by-default.patch + +Patch195: open-iscsi-2.0.874-35-iscsiuio-fix-long-options.patch +Patch196: open-iscsi-2.0.874-38-Update-bnx2.c.patch +Patch197: open-iscsi-2.0.874-39-Update-bnx2x.c.patch +Patch198: open-iscsi-2.0.875-7-Ignore-library-file-for-iscsiuio-src.patch +Patch199: open-iscsi-2.0.875-9-Remove-unused-variables.-No-functional-change.patch +Patch200: open-iscsi-2.0.875-10-Include-sys-sysmacros.h-to-properly-define-minor.patch +Patch201: open-iscsi-2.0.875-11-Declare-inline-best_match_bufcmp-as-static.patch +Patch202: open-iscsi-2.0.875-14-Check-for-root-peer-user-for-iscsiuio-IPC.patch +Patch203: open-iscsi-2.0.875-15-iscsiuio-should-ignore-bogus-iscsid-broadcast-packets.patch +Patch204: open-iscsi-2.0.875-16-Ensure-all-fields-in-iscsiuio-IPC-response-are-set.patch +Patch205: open-iscsi-2.0.875-17-Do-not-double-close-IPC-file-stream-to-iscsid.patch +Patch206: open-iscsi-2.0.875-18-Ensure-strings-from-peer-are-copied-correctly.patch +Patch207: open-iscsi-2.0.875-19-Skip-useless-strcopy-and-validate-CIDR-length.patch +Patch208: open-iscsi-2.0.875-20-Check-iscsiuio-ping-data-length-for-validity.patch +Patch209: open-iscsi-2.0.875-21-tell-git-to-ignore-the-iscsiuio-binary.patch +Patch210: open-iscsi-2.0.875-30-Cleanup-iscsiuio-master-Makefile-template.patch +Patch211: open-iscsi-2.0.876-5-bnx2x.c-Reorder-the-includes-to-avoid-duplicate-defines-with-musl.patch +Patch212: open-iscsi-2.0.876-6-Use-correct-size-when-copying-nic-name.patch +Patch213: 0001-Keep-iscsi_if-in-sync-with-kernel-version.patch +Patch214: 0001-Add-error-message-for-new-ISCSI_ERR_NOP_TIMEDOUT.patch +Patch215: open-iscsi-2.0.876-56-iscsiuio-Release-xmit_mutex-in-error-code-path.patch +Patch216: open-iscsi-2.0.876-57-iscsiuio-limit-retries-of-performing-dhcpv6-before-declaring-dhcp-failure.patch +Patch217: open-iscsi-2.0.876-58-iscsiuio-Do-not-flush-tx-queue-on-each-uio-interrupt.patch +Patch218: open-iscsi-2.0.876-59-qedi-Set-buf_size-in-case-of-ICMP-and-ARP-packet.patch +Patch219: open-iscsi-2.0.876-60-qedi-Use-uio-BD-index-instead-on-buffer-index.patch +Patch220: open-iscsi-2.0.876-61-iscsiuio-v0.7.8.5.patch +Patch221: open-iscsi-2.0.876-66-Close-file-handles-when-writing-pid-files.patch +Patch222: open-iscsi-2.0.876-66-iscsiuio-avoid-loosing-bad-rc-in-nic_nl_open.patch +Patch223: open-iscsi-2.0.876-67-iscsiuio-fail-on-nic_nl_open-failing.patch +Patch224: 0001-rec-update-disable-the-idbm_lock-in-read-write-when-.patch +Patch225: 0001-iscsiuio-allow-processing-of-iscsid-requests-in-DHCP.patch +Patch226: 0001-iscsiuio-update-version-to-0.7.8.6.patch +Patch227: 0001-iscsid-Update-boot-gateway-information-during-sync_s.patch + +# distro specific modifications +Patch351: 0151-update-initscripts-and-docs.patch +Patch352: 0152-use-var-for-config.patch +Patch353: 0153-use-red-hat-for-name.patch +Patch354: 0154-add-libiscsi.patch +Patch356: 0156-remove-the-offload-boot-supported-ifdef.patch +Patch359: 0159-iscsiuio-systemd-unit-files.patch +Patch360: 0160-use-systemctl-to-start-iscsid.patch +Patch361: 0161-resolve-565245-multilib-issues-caused-by-doxygen.patch +Patch362: 0162-Don-t-check-for-autostart-sessions-if-iscsi-is-not-u.patch +Patch364: 0164-libiscsi-fix-incorrect-strncpy-use.patch +Patch366: 0166-start-socket-listeners-on-iscsiadm-command.patch +Patch367: 0167-Revert-iscsiadm-return-error-when-login-fails.patch +Patch368: 0168-update-handling-of-boot-sessions.patch +Patch369: 0169-update-iscsi.service-for-boot-session-recovery.patch +Patch370: 0170-fix-systemd-unit-wants.patch +Patch372: 0172-move-cleanup-to-seperate-service.patch +Patch375: open-iscsi-2.0.876-41-vlan-setting-sync-across-ipv4-ipv6-for-be2iscsi.patch +Patch376: 0001-enable-MaxOutstandingR2T-negotiation.patch + +# upstream removed internal open-isns, but not taking that here just yet +# it requires repackaging isns-utils to provide a debug package +Patch380: keep-open-isns.patch +# version string, needs to be updated with each build +Patch400: 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 +* Wed Sep 11 2019 Chris Leech - 6.2.0.874-14 +- 1598647 update boot gateway information during sync_session + +* Fri Aug 23 2019 Chris Leech - 6.2.0.874-13 +- 1724579 iscsiuio update, allow processing requests in DHCP failure condition + +* Tue Aug 20 2019 Chris Leech - 6.2.0.874-12 +- 1389071 handle newer ISCSI_ERR_NOP_TIMEDOUT code from the kernel +- 1624670 Login negotiation failed due to CHAP_N values do not match + (rec update race condition) + +* Wed Mar 06 2019 Chris Leech - 6.2.0.874-11 +- 1649401, 1649413 iscsiuio updates + +* Wed Aug 29 2018 Chris Leech - 6.2.0.874-10 +- 1185734 set iscsid.safe_logout to Yes by default + +* Fri Jun 22 2018 Chris Leech - 6.2.0.874-9 +- 1578984 update iscsiuio to v0.7.8.4 + +* Fri Jun 22 2018 Chris Leech - 6.2.0.874-8 +- 1278438 enable MaxOutstandingR2T negotiation during login + +* Thu Nov 30 2017 Chris Leech - 6.2.0.874-7 +- 1328694 keep vlan settings in sync for ipv4/ipv6 iface records with be2iscsi + +* Wed Nov 01 2017 Chris Leech - 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 - 6.2.0.874-5 +- 1431622 fix default in iscsi-iname manpage to match Red Hat customization + +* Tue Jun 27 2017 Chris Leech - 6.2.0.874-4 +- 1450756 isolate iscsistart sockets + +* Fri Apr 28 2017 Chris Leech - 6.2.0.874-3 +- 1445686 add missing ping hook for the qedi transport driver + +* Tue Apr 11 2017 Chris Leech - 6.2.0.874-2 +- 1422941 allow disabling of auto scanning sessions, requested for OpenStack + +* Tue Feb 28 2017 Chris Leech - 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 - 6.2.0.873-35 +- 1362590 Revert iscsiuio pthread changes that result in a race condition on shutdown + +* Tue Jun 14 2016 Chris Leech - 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 - 6.2.0.873-33 +- 1275139 iscsiuio support for multi-function mode NetXtreme2 HBAs + +* Fri Jul 24 2015 Chris Leech - 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 - 6.2.0.873-31 +- 1235684 fix safe logout DM name canonicalization, use libmount cache + +* Mon Jul 06 2015 Chris Leech - 6.2.0.873-30 +- 1235684 add iscsid safe logout option + +* Fri Jan 30 2015 Chris Leech - 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 - 6.2.0.873-28 +- 1180100 scriptlets were never split out properly for the iscsiuio subpackage + +* Thu Jan 15 2015 Chris Leech - 6.2.0.873-27 +- 1168556 fix regression in network interface binding + +* Mon Jan 12 2015 Chris Leech - 6.2.0.873-26 +- 1166713 created iscsi-shutdown.service to ensure that session cleanup happens + +* Thu Dec 11 2014 Andy Grover - 6.2.0.873-25 +- Add --with-slp=no for #1088020 + +* Tue Nov 18 2014 Chris Leech - 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 - 6.2.0.873-23 +- make sure to pass --with-security=no to isns configure (#1088020) + +* Wed Sep 24 2014 Chris Leech - 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 - 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 - 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 - 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 - 6.2.0.873-18 +- Mass rebuild 2014-01-24 + +* Fri Dec 27 2013 Daniel Mach - 6.2.0.873-17 +- Mass rebuild 2013-12-27 + +* Mon Nov 25 2013 Chris Leech - 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 - 6.2.0.873-15 +- move /sbin to /usr/sbin +- use rpm macros in install rules + +* Fri Sep 13 2013 Chris Leech - 6.2.0.873-14 +- fix iscsiuio hardened build and other compiler flags + +* Fri Aug 23 2013 Andy Grover - 6.2.0.873-13 +- Fix patch 0041 to check session != NULL before calling iscsi_sysfs_read_boot() + +* Tue Aug 20 2013 Chris Leech - 6.2.0.873-12 +- fix regression in last build, database records can't be accessed + +* Mon Aug 19 2013 Chris Leech - 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 - 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 6.2.0.873-9 +- Fix FTBFS, cleanup spec + +* Sat Aug 03 2013 Fedora Release Engineering - 6.2.0.873-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Tue Jun 11 2013 Chris Leech - 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 - 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 - 6.2.0.873-5 +- Fix typo in NM dispatcher script (bug #917058) + +* Thu Feb 21 2013 Chris Leech - 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 - 6.2.0.873-3 +- Fix the postun script to not use ldconfig as the interpreter + +* Wed Jan 23 2013 Chris Leech - 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 - 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 - 6.2.0.872-19 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Tue Feb 14 2012 Mike Christie 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 6.2.0.872.17 +- 786174 Change iscsid/iscsi service startup, so it always starts + when called. + +* Sat Feb 4 2012 Mike Christie 6.2.0.872.16 +- 747479 Fix iscsidevs handling of network requirement + +* Fri Jan 13 2012 Fedora Release Engineering - 6.2.0.872-15 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Wed Nov 30 2011 Mike Christie 6.2.0.872.14 +- Fix version string to reflect fedora and not rhel. + +* Tue Oct 18 2011 Mike Christie 6.2.0.872.13 +- Update iscsi tools. + +* Sat Apr 30 2011 Hans de Goede - 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 - 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 6.2.0.872-10 +- Fix iscsi init scripts check for networking being up (#692230) + +* Wed Feb 09 2011 Fedora Release Engineering - 6.2.0.872-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild