summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoropen-iscsi <open-iscsi@d7303112-9cec-0310-bdd2-e83a94d6c2b6>2005-01-18 02:22:39 +0000
committeropen-iscsi <open-iscsi@d7303112-9cec-0310-bdd2-e83a94d6c2b6>2005-01-18 02:22:39 +0000
commit1fd764eeeea0c8c8281c1be1fd6e6f6e5a33f81a (patch)
tree1708d173ad349f26ce51280764dba538aabf402f
parent761b07cc9f4e75522111d1b588326b7c7f7af346 (diff)
downloadopen-iscsi-1fd764eeeea0c8c8281c1be1fd6e6f6e5a33f81a.tar.gz
kernel/user compiles
git-svn-id: svn://svn.berlios.de/open-iscsi@34 d7303112-9cec-0310-bdd2-e83a94d6c2b6
-rw-r--r--etc/iscsid.conf510
-rw-r--r--include/iscsi_proto.h20
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/iscsi_if.h2
-rw-r--r--kernel/iscsi_mgr.c4
-rw-r--r--kernel/iscsi_tcp.c16
-rw-r--r--usr/Makefile5
-rw-r--r--usr/auth.c6
-rw-r--r--usr/config.c3278
-rw-r--r--usr/config.h349
-rw-r--r--usr/discovery.c1785
-rw-r--r--usr/discovery.h17
-rw-r--r--usr/initiator.h21
-rw-r--r--usr/io.c14
-rw-r--r--usr/iscsiadm.c4
-rw-r--r--usr/iscsiadm.h54
-rw-r--r--usr/iscsid.c5
-rw-r--r--usr/iscsid.h15
-rw-r--r--usr/login.c58
-rw-r--r--usr/md5.h2
-rw-r--r--usr/strings.c288
-rw-r--r--usr/strings.h46
22 files changed, 6391 insertions, 110 deletions
diff --git a/etc/iscsid.conf b/etc/iscsid.conf
index 5480e96..b56c864 100644
--- a/etc/iscsid.conf
+++ b/etc/iscsid.conf
@@ -1,23 +1,487 @@
-target_name = iqn.2001-04.com.example:storage.disk2.sys1.xyz
-target_portal = 10.16.16.227:3260,1
-login_user = dima
-login_password = aloha
-initiator_name = iqn.com.dima
-initiator_alias = dima-um
-isid = 0x80.0x0000.0x00.0x0001
-first_burst = 262144
-max_recv_dlength = 65536
-max_burst = 262144
-max_r2t = 1
-max_cnx = 1
-erl = 0
-initial_r2t_en = 0
-imm_data_en = 1
-hdrdgst_en = 0
-datadgst_en = 0
-ifmarker_en = 0
-ofmarker_en = 0
-pdu_inorder_en = 1
-dataseq_inorder_en = 1
-time2wait = 5
-time2retain = 20
+# ============================================================================
+# iSCSI Configuration File Sample - see iscsi.conf(5)
+# ============================================================================
+#
+# All of the configuration parameters described in this file are applied
+# globally to all targets, unless they are overridden by a local setting. The
+# three types of local categories that can override the global settings are:
+#
+# Target Name (i.e., TargetName)
+# Network (i.e., Subnet or Address)
+# SCSI Routing Instance (i.e., DiscoveryAddress)
+#
+# All parameters that are localized to one of the categories above must be
+# indented by at least one white space or a tab character. If the parameter is
+# not indented, it will be interpreted as a global parameter (see examples for
+# each parameter).
+#
+# If more that one entry exists for any given parameter (either global or
+# local), the last entry has precedence.
+#
+# If a parameter setting under the network category conflicts with a different
+# setting of the same parameter under the discovery address or target name
+# category (for the same target), the network setting will have precedence.
+#
+# If a parameter is not specified in the iscsi.conf file, the default setting is
+# used. The default values for all parameters can be found in the readme file.
+#
+# In the sample settings shown below, the following definitions apply:
+# <text> = any alpha-numeric text string
+# <number> = any numeric text string
+# <address> = valid IP address of the form a.b.c.d[/e]
+# <portal> = valid portal address of the form a.b.c.d[:e]
+#
+# ----------------
+# Network Category
+# ----------------
+# To localize parameters to targets on a particular network (i.e., to
+# override the global settings), you need to use either the "Subnet" or
+# "Address" settings. The format for the "Subnet" setting is a.b.c.d/e. In
+# addition, multiple subnets can be specified by using a "," delimiter. An
+# example of these settings would be:
+#
+#Subnet=10.4.100.0/24
+# or
+#Subnet=10.4.100.1/24,10.5.100.1/24
+#
+# The format for "Address" is a.b.c.d and it too can have multiple values using
+# a "," delimiter. An example of these settings would be:
+#
+#Address=10.4.100.0
+# or
+#Address=10.5.101.4,10.5.101.5
+#
+# The following parameters can be specified using the network category:
+#
+# 1) Connection Timeout Settings
+# 2) Error Handling Timeout Settings
+# 3) TCP Settings
+#
+# --------------------------
+# Discovery Address Category
+# --------------------------
+# To localize parameters to targets found on a particular discovery address
+# (i.e., to override the global settings), you need to use the
+# "DiscoveryAddress" setting. The format for the "DiscoveryAddress" setting is
+# a.b.c.d, a.b.c.d:e (where e is a TCP port number), or an instance name. An
+# example of these settings would be:
+#
+#DiscoveryAddress=10.4.100.0
+# or
+#DiscoveryAddress=10.4.100.1:3260
+# or
+#DiscoveryAddress=scisrouter1
+#
+# The following parameters can be specified using the discovery address
+# category:
+#
+# 1) Authentication Settings
+# 2) ConnectionTimeout Settings
+# 3) Continuous Discovery settings
+# 4) AsyncEvent Notification Settings
+#
+# --------------------
+# Target Name Category
+# --------------------
+# To localize parameters to targets identified by a particular target name
+# (i.e., to override the global settings), you need to use the
+# "TargetName" setting. The format for the "TargetName" setting is
+# either the 'iqn' or 'eui' format. An example of these settings would be:
+#
+#TargetName=iqn.1987-05.com.cisco:00.0d1d898e8d66.t0
+#
+# The following parameters can be specified using the target name category:
+#
+# 1) CRC Settings
+# 2) iSCSI Operational Parameter settings
+# 3) Connection Timeout Settings
+# 4) Session Timeout Settings
+# 5) Error Handling Timeout Settings
+# 6) TCP Settings
+# 7) Enable/Disable targets
+#
+# ============================================================================
+# PARAMETERS
+# ============================================================================
+#
+# -----------------------
+# AUTHENTICATION SETTINGS
+# -----------------------
+# To globally configure a CHAP username and password for initiator
+# authentication by the target(s), uncomment the following lines:
+#
+#OutgoingUsername=<text>
+#OutgoingPassword=<text>
+#
+# The maximum length for both the password and username is 256 characters.
+
+# An example username and password would be:
+#
+#OutgoingUsername=alice
+#OutgoingPassword=nty57nbe
+#
+# To globally configure a CHAP username and password for target(s)
+# authentication by the initiator, uncomment the following lines:
+#
+#IncomingUsername=<text>
+#IncomingPassword=<text>
+#
+# The maximum length for both the password and username is 256 characters.
+
+# An example username and password would be:
+#
+#IncomingUsername=bill
+#IncomingPassword=ghot67
+#
+# The global authentication settings can be overridden on a per discovery
+# address basis. An example of a unique username and password for all targets
+# found at address 192.168.10.94 would be:
+#
+#DiscoveryAddress=192.168.10.94
+# OutgoingUsername=fred
+# OutgoingPassword=uhyt6h
+# and/or
+#
+#DiscoveryAddress=192.168.10.94
+# IncomingUsername=mary
+# IncomingPassword=kdhjkd9l
+#
+# ---------------
+# DIGEST SETTINGS
+# ---------------
+# To globally enable CRC32C digest checking for the header and/or data part of
+# iSCSI PDUs, uncomment one or both of the following lines:
+#
+#HeaderDigest=always
+#DataDigest=always
+#
+# To globally disable digest checking for the header and/or data part of
+# iSCSI PDUs, uncomment one or both of the following lines:
+#
+#HeaderDigest=never
+#DataDigest=never
+#
+# To globally 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:
+#
+#HeaderDigest=prefer-on
+#DataDigest=prefer-on
+#
+# To globally 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:
+#
+#HeaderDigest=prefer-off
+#DataDigest=prefer-off
+#
+# The global digest settings can be overridden on a per target name basis. An
+# example of enabling header and data digest checking just for target iqn.1987-
+# 05.com.cisco:00.0d1d898e8d66.t0 would be:
+#
+#TargetName=iqn.1987-05.com.cisco:00.0d1d898e8d66.t0
+# HeaderDigest=always
+# DataDigest=always
+#
+# It should also be noted that if the initiator and the target have incompatible
+# settings (e.g., target set for "always" and initiator set for "never"), the
+# login will fail.
+#
+# ----------------------
+# ENABLE/DISABLE TARGETS
+# ----------------------
+# To globally enable/disable group of targets use the following option.
+#
+# TargetNames mentioned after the below entry will be enabled by default.
+#Enabled=yes
+#
+# TargetNames mentioned after the below entry will be disabled by default.
+#Enabled=no
+#
+# To specifically enable/disable a target, use the following entry
+# under Targetname.
+#
+#TargetName=iqn.1987-05.com.cisco:00.0d1d898e8d66.t0
+# Enabled=yes
+#
+# ---------------------------
+# CONNECTION TIMEOUT SETTINGS
+# ---------------------------
+# To globally specify the time to wait for a login PDU to be received from
+# the target in response to a login request sent by the initiator before failing
+# the connection attempt, uncomment the following line:
+#
+#LoginTimeout=<number>
+#
+# where <number> is in seconds. A setting of "0" will result in commands never
+# being timed out.
+#
+# To globally specify the time to wait for a login PDU carrying authentication
+# information to be received from the target in response to a login request sent
+# by the initiator before failing the connection attempt, uncomment the
+# following line:
+#
+#AuthTimeout=<number>
+#
+# where <number> is in seconds. A setting of "0" will result in commands never
+# being timed out.
+#
+# To globally specify the time to wait on a connection with no traffic being
+# received from the target before checking the connection by sending out a ping,
+# uncomment the following line:
+#
+#IdleTimeout=<number>
+#
+# where <number> is in seconds. A setting of "0" will result in a ping never
+# being sent.
+#
+# To globally specify the time to wait for a ping response after a ping has been
+# sent to a target before failing the existing connection and initiating a new
+# one, uncomment the following line:
+#
+#PingTimeout=<number>
+#
+# where <number> is in seconds. A setting of "0" will result in the ping command
+# never timing out.
+#
+# The global connection timeout settings can be overridden on a per target name,
+# discovery address or IP address basis. An example of setting the
+# "LoginTimeout" value to 12 seconds for just target iqn.1987-
+# 05.com.cisco:00.0d1d898e8d66.t0 would be:
+#
+#TargetName=iqn.1987-05.com.cisco:00.0d1d898e8d66.t0
+# LoginTimeout=12
+#
+# An example of setting the "AuthTimeout" value to 8 seconds for just all
+# targets found at address 192.168.10.94 would be:
+#
+#DiscoveryAddress=192.168.10.94
+# AuthTimeout=8
+#
+# An example of setting the "IdleTimeout" value to 3 seconds for just all
+# targets found on subnet 192.168.10.94 would be:
+#
+#Subnet=192.168.10.0/24
+# IdleTimeout=3
+#
+# ------------------------
+# SESSION TIMEOUT SETTINGS
+# ------------------------
+# To globally specify the length of time to wait for session re-establishment
+# before failing SCSI commands back to the application, uncomment the
+# following line:
+#
+#ConnFailTimeout=<number>
+#
+# where <number> is in seconds. A setting of "0" will result in commands never
+# being failed back due to connection failure.
+#
+# The global session timeout settings can be overridden on a per target name
+# basis. An example of setting the "ConnFailTimeout" value to 5 seconds for
+# just target iqn.1987-05.com.cisco:00.0d1d898e8d66.t0 would be:
+#
+#TargetName=iqn.1987-05.com.cisco:00.0d1d898e8d66.t0
+# ConnFailTimeout=5
+#
+# If a third party multipathing application is being used,
+# then the "ConnFailTimeout" should be set to smaller value
+# such as 15. This value is just a guideline so the actual value will be
+# dependent on the users operating environment.
+#
+# -------------------------------
+# ERROR HANDLING TIMEOUT SETTINGS
+# -------------------------------
+# To globally specify the length of time to wait for an abort command to
+# complete before declaring the abort command has failed, uncomment the
+# following line:
+#
+#AbortTimeout=<number>
+#
+# where <number> is in seconds. A setting of "0" will result in commands never
+# being timed out.
+#
+# To globally specify the length of time to wait for a reset command to complete
+# before declaring that the reset command has failed, uncomment the following
+# line:
+#
+#ResetTimeout=<number>
+#
+# where <number> is in seconds. A setting of "0" will result in commands never
+# being timed out.
+#
+# The global error handling timeout settings can be overridden on a per target
+# name or per IP address basis. An example of setting the "AbortTimeout" value
+# to 10 seconds for just target iqn.1987-05.com.cisco:00.0d1d898e8d66.t0 would
+# be:
+#
+#TargetName=iqn.1987-05.com.cisco:00.0d1d898e8d66.t0
+# AbortTimeout=10
+#
+# An example of setting the "ResetTimeout" value to 6 seconds for just all
+# targets found on portal 192.168.10.94 would be:
+#
+#Subnet=192.168.10.0/24
+# ResetTimeout=6
+#
+# -----------------------------
+# CONTINUOUS DISCOVERY SETTINGS
+# -----------------------------
+# To globally specify that all discovery sessions be kept open, uncomment the
+# following line:
+#
+#Continuous=yes
+#
+# To globally specify that all discovery sessions be closed once discovery is
+# completed, uncomment the following line:
+#
+#Continuous=no
+#
+# The global continuous discovery setting can be overridden on a per target
+# basis. An example of setting "Continuous" to "no" for just target iqn.1987-
+# 05.com.cisco:00.0d1d898e8d66.t0 would be:
+#
+#TargetName=iqn.1987-05.com.cisco:00.0d1d898e8d66.t0
+# Continuous=no
+#
+# ---------------------------------
+# ASYNC EVENT NOTIFICATION SETTINGS
+# ---------------------------------
+# To globally specify that the initiator wants to receive vendor specific async
+# events from the target(s), uncomment the following line:
+#
+#SendAsyncText=yes
+#
+# To globally specify that the initiator does not want to receive vendor
+# specific async events from the target(s), uncomment the following line:
+#
+#SendAsyncText=no
+#
+# The SendAsyncText key can be specified for a particular Discovery Address.
+#
+# The global async event notification setting can be overridden on a per target
+# basis. An example of setting "SendAsyncText" to "no" for just target iqn.1987-
+# 05.com.cisco:00.0d1d898e8d66.t0 would be:
+#
+#TargetName=iqn.1987-05.com.cisco:00.0d1d898e8d66.t0
+# SendAysncText=no
+#
+# ------------------------------------
+# iSCSI OPERATIONAL PARAMETER SETTINGS
+# ------------------------------------
+# To globally enable R2T flow control (i.e., the initiator must wait for an R2T
+# command before sending any data), uncomment the following line:
+#
+#InitialR2T=yes
+#
+# To globally disable R2T flow control (i.e., the initiator has an implied
+# initial R2T of "FirstBurstLength" at offset 0), uncomment the following line:
+#
+#InitialR2T=no
+#
+# To globally enable immediate data (i.e., the initiator sends unsolicited data
+# with the iSCSI command packet), uncomment the following line:
+#
+#ImmediateData=yes
+#
+# To globally disable immediate data (i.e., the initiator does not send
+# unsolicited data with the iSCSI command PDU), uncomment the following line:
+#
+#ImmediateData=no
+#
+# To globally specify the maximum number of data bytes the initiator can receive
+# in an iSCSI PDU from a target, uncomment the following line:
+#
+#MaxRecvDataSegmentLength=<number>
+#
+# where <number> is the number of bytes in the range of 512 to (2^24-1)
+#
+# To globally specify the maximum number of unsolicited data bytes the initiator
+# can send in an iSCSI PDU to a target, uncomment the following line:
+#
+#FirstBurstLength=<number>
+#
+# where <number> is the number of bytes in the range of 512 to (2^24-1)
+#
+# To globally specify the maximum SCSI payload that the initiator will negotiate
+# with the target for, uncomment the following line:
+#
+#MaxBurstLength=<number>
+#
+# where <number> is the number of bytes in the range of 512 to (2^24-1)
+#
+# To globally specifiy the maximum number of bytes that can be sent over a TCP
+# connection by the initiator before receiving an acknowledgement from the
+# target, uncomment the following line:
+#
+#TCPWindowSize=<number>
+#
+# where <number> is the number of bytes in the range of 512 to (2^24-1)
+#
+# The global iSCSI operational parameter setting can be overridden on a per
+# target basis. An example of setting multiple parameters for just target
+# iqn.1987-05.com.cisco:00.0d1d898e8d66.t0 would be:
+#
+#TargetName=iqn.1987-05.com.cisco:00.0d1d898e8d66.t0
+# InitialR2T=no
+# ImmediateData=no
+# MaxRecvDataSegmentLength=128 * 1024
+# FirstBurstLength=262144
+# MaxBurstLength=(16 * 1024 * 1024) - 1024
+# TCPWindowSize=262144
+#
+# The global "TCPWindowSize" setting can also be overridden on a per portal
+# basis. An example of setting the "TCPWindowSize" for just subnet 10.77.13.0/16
+# would be:
+#
+#Subnet=10.77.13.0/16
+# TCPWindowSize=262144
+#
+# ------------
+# SLP SETTINGS
+# ------------
+# To globally configure the unicast IP address of the SLP service or directory
+# agent (i.e., the address at which iSCSI targets can be discovered), uncomment
+# the following line:
+#
+#SLPUnicast=<address>
+#
+# where <address> is single IP address.
+#
+# To globally configure the multicast IP address of the SLP service or directory
+# agent (i.e., the address at which iSCSI targets can be discovered), uncomment
+# the following line:
+#
+#SLPMulticast=<address>
+#
+# where <address> is one of the following values:
+# "all"
+# "none"
+# a comma delimited list of IP addresses
+#
+# An example of valid SLPMulticast settings are:
+#
+#SLPMulticast=all
+#SLPMulticast=none
+#SLPMulticast=192.168.10.94,10.77.10.94
+#
+# To enable CHAP authentication for every target discovered through a given SLP
+# directory or service agent, add an "OutgoingUsername" and "OutgoingPassword"
+# entry indented below the "SLPUnicast" or "SLPMulticast" entries. An example of
+# these configurations would be:
+#
+#SLPUnicast=192.168.10.95
+# OutgoingUsername=alice
+# OutgoingPassword=nty57nbe
+#
+#SLPMulticast=all
+# OutgoingUsername=alice1
+# OutgoingPassword=nty57ocf
+#
+# To specify the time interval between the sending of successive SLP queries,
+# uncomment the following line:
+#
+#PollInterval=<text>
+#
+# where <text> is specified in either seconds (e.g., "30s"), minutes
+# (e.g., "3m") or hours (e.g., "5h").
+
diff --git a/include/iscsi_proto.h b/include/iscsi_proto.h
index a5c1ff9..172e94d 100644
--- a/include/iscsi_proto.h
+++ b/include/iscsi_proto.h
@@ -85,7 +85,7 @@ typedef struct iscsi_hdr {
#define ISCSI_OP_LOGOUT_RSP 0x26
#define ISCSI_OP_R2T 0x31
#define ISCSI_OP_ASYNC_EVENT 0x32
-#define ISCSI_OP_REJECT_MSG 0x3f
+#define ISCSI_OP_REJECT 0x3f
/* SCSI Command Header */
typedef struct iscsi_cmd {
@@ -168,13 +168,13 @@ typedef struct iscsi_async {
uint8_t rsvd5[4];
} iscsi_async_t;
-/* iSCSI Event Indicator values */
-#define ASYNC_EVENT_SCSI_EVENT 0
-#define ASYNC_EVENT_REQUEST_LOGOUT 1
-#define ASYNC_EVENT_DROPPING_CONNECTION 2
-#define ASYNC_EVENT_DROPPING_ALL_CONNECTIONS 3
-#define ASYNC_EVENT_PARAM_NEGOTIATION 4
-#define ASYNC_EVENT_VENDOR_SPECIFIC 255
+/* iSCSI Event Codes */
+#define ISCSI_ASYNC_MSG_SCSI_EVENT 0
+#define ISCSI_ASYNC_MSG_REQUEST_LOGOUT 1
+#define ISCSI_ASYNC_MSG_DROPPING_CONNECTION 2
+#define ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS 3
+#define ISCSI_ASYNC_MSG_PARAM_NEGOTIATION 4
+#define ISCSI_ASYNC_MSG_VENDOR_SPECIFIC 255
/* NOP-Out Message */
typedef struct iscsi_nopout {
@@ -556,7 +556,7 @@ typedef struct iscsi_snack {
#define ISCSI_FLAG_SNACK_TYPE_MASK 0x0F /* 4 bits */
/* Reject Message Header */
-typedef struct iscsi_reject_rsp {
+typedef struct iscsi_reject {
uint8_t opcode;
uint8_t flags;
uint8_t reason;
@@ -570,7 +570,7 @@ typedef struct iscsi_reject_rsp {
uint32_t datasn;
uint8_t rsvd5[8];
/* Text - Rejected hdr */
-} iscsi_reject_rsp_t;
+} iscsi_reject_t;
/* Reason for Reject */
#define CMD_BEFORE_LOGIN 1
diff --git a/kernel/Makefile b/kernel/Makefile
index 4afab0f..865bcc6 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -2,7 +2,7 @@
# Makefile for the Linux Kernel iSCSI Initiator
#
-EXTRA_CFLAGS += -I$(obj)
+EXTRA_CFLAGS += -I$(obj) -I$(obj)/../include
obj-m = iscsi.o
iscsi-y = iscsi_mgr.o iscsi_tcp.o
diff --git a/kernel/iscsi_if.h b/kernel/iscsi_if.h
index 4d2e83b..8924cb2 100644
--- a/kernel/iscsi_if.h
+++ b/kernel/iscsi_if.h
@@ -20,7 +20,7 @@
#define ISCSI_IF_H
#include <net/tcp.h>
-#include <iscsi.h>
+#include <iscsi_proto.h>
typedef void* iscsi_snx_h; /* iSCSI Data-Path session handle */
typedef void* iscsi_cnx_h; /* iSCSI Data-Path connection handle */
diff --git a/kernel/iscsi_mgr.c b/kernel/iscsi_mgr.c
index c8547bc..7c104fd 100644
--- a/kernel/iscsi_mgr.c
+++ b/kernel/iscsi_mgr.c
@@ -20,7 +20,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_transport.h>
#include <iscsi_if.h>
-#include <iscsi_control.h>
+#include <iscsi_mgr.h>
static iscsi_provider_t provider_table[ISCSI_PROVIDER_MAX];
@@ -715,7 +715,7 @@ iscsi_control_recv_pdu(iscsi_cnx_h handle, iscsi_hdr_t *hdr, char *data)
case ISCSI_OP_TEXT_RSP:
case ISCSI_OP_LOGOUT_RSP:
case ISCSI_OP_ASYNC_EVENT:
- case ISCSI_OP_REJECT_MSG:
+ case ISCSI_OP_REJECT:
break;
default:
return -EPERM;
diff --git a/kernel/iscsi_tcp.c b/kernel/iscsi_tcp.c
index 2816e71..1ff2296 100644
--- a/kernel/iscsi_tcp.c
+++ b/kernel/iscsi_tcp.c
@@ -91,6 +91,9 @@ static int iscsi_proc_info(struct Scsi_Host *host, char *buffer, char **start,
* *
******************************************************************************/
+/*
+ * Insert before consumer pointer
+ */
static void
__insert(iscsi_queue_t *queue, void *data)
{
@@ -105,17 +108,6 @@ __insert(iscsi_queue_t *queue, void *data)
queue->pool[--queue->cons] = data;
}
-/*
- * Insert before consumer pointer
- */
-static void
-iscsi_insert(iscsi_queue_t *queue, void *data)
-{
- spin_lock_bh(queue->lock);
- __insert(queue, data);
- spin_unlock_bh(queue->lock);
-}
-
static void
__enqueue(iscsi_queue_t *queue, void *data)
{
@@ -1027,7 +1019,7 @@ iscsi_hdr_recv(iscsi_conn_t *conn)
case ISCSI_OP_TEXT_RSP:
case ISCSI_OP_LOGOUT_RSP:
case ISCSI_OP_ASYNC_EVENT:
- case ISCSI_OP_REJECT_MSG:
+ case ISCSI_OP_REJECT:
if (!conn->in.datalen) {
rc = iscsi_control_recv_pdu(
conn->handle, hdr, NULL);
diff --git a/usr/Makefile b/usr/Makefile
index 77e4b47..f9b0642 100644
--- a/usr/Makefile
+++ b/usr/Makefile
@@ -1,12 +1,13 @@
CFLAGS += -O2 -fno-inline -Wall -Wstrict-prototypes -g -I../include
PROGRAMS = iscsid iscsiadm
+COMMON_SRCS = io.o auth.o login.o ctldev.o log.o md5.o sha1.o
all: $(PROGRAMS)
-iscsid: io.o auth.o login.o iscsid.o ctldev.o log.o md5.o sha1.o ipc.o
+iscsid: $(COMMON_SRCS) iscsid.o ipc.o
$(CC) $^ -o $@
-iscsiadm: iscsiadm.o ctldev.o
+iscsiadm: $(COMMON_SRCS) strings.o config.o discovery.o iscsiadm.o
$(CC) $^ -o $@
clean:
diff --git a/usr/auth.c b/usr/auth.c
index 1411e45..418a379 100644
--- a/usr/auth.c
+++ b/usr/auth.c
@@ -99,7 +99,7 @@ acl_chap_auth_request(struct iscsi_acl *client, char *username, unsigned int id,
unsigned char *response_data,
unsigned int rsp_length)
{
- struct iscsi_session *session = client->session_handle;
+ iscsi_session_t *session = client->session_handle;
struct MD5Context context;
unsigned char verify_data[16];
@@ -1323,7 +1323,7 @@ acl_recv_begin(struct iscsi_acl *client)
}
int
-acl_recv_end(struct iscsi_acl *client, struct iscsi_session *session_handle)
+acl_recv_end(struct iscsi_acl *client, iscsi_session_t *session_handle)
{
int next_phase_flag = 0;
@@ -1337,7 +1337,7 @@ acl_recv_end(struct iscsi_acl *client, struct iscsi_session *session_handle)
client->phase = AUTH_PHASE_ERROR;
return AUTH_STATUS_ERROR;
}
-
+
if (client->recv_end_count > AUTH_RECV_END_MAX_COUNT) {
client->rmt_auth_status = AUTH_STATUS_FAIL;
client->phase = AUTH_PHASE_DONE;
diff --git a/usr/config.c b/usr/config.c
new file mode 100644
index 0000000..7908cd7
--- /dev/null
+++ b/usr/config.c
@@ -0,0 +1,3278 @@
+/*
+ * iSCSI Configuration Reader
+ *
+ * Copyright (C) 2002 Cisco Systems, Inc.
+ * maintained by linux-iscsi-devel@lists.sourceforge.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * See the file COPYING included with this distribution for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "config.h"
+#include "log.h"
+
+#define CHAP_AUTHENTICATION 1
+
+static void
+iscsi_init_config_defaults(struct iscsi_config_defaults *defaults)
+{
+ /* enabled/disabled */
+ defaults->enabled = 1;
+
+ /* discovery defaults */
+ defaults->continuous_sendtargets = 1; /* auto detect */
+ defaults->send_async_text = 1;
+#ifdef SLP_ENABLE
+ defaults->slp_multicast = 1;
+#else
+ defaults->slp_multicast = 0;
+#endif
+ defaults->slp_scopes = NULL;
+ defaults->slp_poll_interval = 5 * 60; /* 5 minutes */
+
+ /* auth options */
+ defaults->auth_options.authmethod = CHAP_AUTHENTICATION;
+ defaults->auth_options.password_length = 0;
+ defaults->auth_options.password_length_in = 0;
+
+ /* connection timeouts */
+ defaults->connection_timeout_options.login_timeout = 15;
+ defaults->connection_timeout_options.auth_timeout = 45;
+ defaults->connection_timeout_options.active_timeout = 5;
+ defaults->connection_timeout_options.idle_timeout = 60;
+ defaults->connection_timeout_options.ping_timeout = 5;
+
+ /* error timeouts */
+ defaults->error_timeout_options.abort_timeout = 10;
+ defaults->error_timeout_options.reset_timeout = 30;
+
+ /* session timeouts */
+ defaults->session_timeout_options.replacement_timeout = 0;
+
+ /* tcp options */
+ defaults->tcp_options.window_size = 256 * 1024;
+
+ /* iSCSI operational parameters */
+ defaults->iscsi_options.InitialR2T = 0;
+ defaults->iscsi_options.ImmediateData = 1;
+ defaults->iscsi_options.MaxRecvDataSegmentLength = 128 * 1024;
+ defaults->iscsi_options.FirstBurstLength = 256 * 1024;
+ defaults->iscsi_options.MaxBurstLength = (16 * 1024 * 1024) - 1024;
+ defaults->iscsi_options.DefaultTime2Wait = 0;
+ /* we only use session reinstatement (ERL 0) */
+ defaults->iscsi_options.DefaultTime2Retain = 0;
+ /* we only use session reinstatement (ERL 0) */
+ defaults->iscsi_options.HeaderDigest = CONFIG_DIGEST_PREFER_OFF;
+ defaults->iscsi_options.DataDigest = CONFIG_DIGEST_PREFER_OFF;
+
+}
+
+char *
+get_iscsi_initiatorname(char *pathname)
+{
+ FILE *f = NULL;
+ int c;
+ char *line, buffer[1024];
+ char *name = NULL;
+
+ if (!pathname) {
+ log_error("No pathname to load InitiatorName from");
+ return NULL;
+ }
+
+ /* get the InitiatorName */
+ if ((f = fopen(pathname, "r"))) {
+ while ((line = fgets(buffer, sizeof (buffer), f))) {
+
+ while (line && isspace(c = *line))
+ line++;
+
+ if (strncmp(line, "InitiatorName=", 14) == 0) {
+ char *end = line + 14;
+
+ /* the name is everything up to the first
+ * bit of whitespace
+ */
+ while (*end && (!isspace(c = *end)))
+ end++;
+
+ if (isspace(c = *end))
+ *end = '\0';
+
+ if (end > line + 14)
+ name = strdup(line + 14);
+ }
+ }
+ fclose(f);
+ if (!name) {
+ log_error(
+ "an InitiatorName is required, but "
+ "was not found in %s", pathname);
+ return NULL;
+ } else {
+ log_debug(5, "InitiatorName=%s\n", name);
+ }
+ return name;
+ } else {
+ log_error("cannot open InitiatorName configuration file %s",
+ pathname);
+ return NULL;
+ }
+}
+
+int
+add_config_entry(struct iscsi_config *config, struct iscsi_config_entry *entry)
+{
+ if (config == NULL || entry == NULL)
+ return 0;
+
+ if (config->head) {
+ entry->prev = config->tail;
+ entry->next = NULL;
+ config->tail->next = entry;
+ config->tail = entry;
+ } else {
+ entry->next = entry->prev = NULL;
+ config->head = config->tail = entry;
+ }
+
+ return 1;
+}
+
+int
+remove_config_entry(struct iscsi_config *config,
+ struct iscsi_config_entry *entry)
+{
+ if (config == NULL || entry == NULL)
+ return 0;
+
+ if (entry == config->head) {
+ config->head = entry->next;
+ if (config->head == NULL)
+ config->tail = NULL;
+ entry->next = entry->prev = NULL;
+ return 1;
+ } else if (entry == config->tail) {
+ entry->prev->next = NULL;
+ config->tail = entry->prev;
+ entry->next = entry->prev = NULL;
+ return 1;
+ } else if (entry->prev && entry->next) {
+ entry->prev->next = entry->next;
+ entry->next->prev = entry->prev;
+ entry->next = entry->prev = NULL;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void
+free_config_entry(struct iscsi_config_entry *entry)
+{
+ if (entry == NULL)
+ return;
+
+ switch (entry->type) {
+ case CONFIG_TYPE_SENDTARGETS:{
+ struct iscsi_sendtargets_config *sendtargets =
+ entry->config.sendtargets;
+
+ if (sendtargets->address) {
+ free(sendtargets->address);
+ sendtargets->address = NULL;
+ }
+
+ free(sendtargets);
+ entry->config.sendtargets = NULL;
+ break;
+ }
+ case CONFIG_TYPE_SLP:{
+ struct iscsi_slp_config *slp = entry->config.slp;
+
+ if (slp->interfaces) {
+ free(slp->interfaces);
+ slp->interfaces = NULL;
+ }
+
+ if (slp->address) {
+ free(slp->address);
+ slp->address = NULL;
+ }
+
+ if (slp->scopes) {
+ free(slp->scopes);
+ slp->scopes = NULL;
+ }
+
+ free(slp);
+ entry->config.slp = NULL;
+ break;
+ }
+ case CONFIG_TYPE_DISCOVERY_FILE:{
+ struct iscsi_discovery_file_config *file =
+ entry->config.file;
+
+ if (file->filename) {
+ free(file->filename);
+ file->filename = NULL;
+ }
+ if (file->address) {
+ free(file->address);
+ file->address = NULL;
+ }
+ if (file->port) {
+ free(file->port);
+ file->port = NULL;
+ }
+
+ free(file);
+ entry->config.file = NULL;
+ break;
+ }
+ case CONFIG_TYPE_TARGETNAME:{
+ struct iscsi_targetname_config *targetname =
+ entry->config.targetname;
+
+ if (targetname->TargetName)
+ free(targetname->TargetName);
+
+ free(targetname);
+ entry->config.targetname = NULL;
+ break;
+ }
+ case CONFIG_TYPE_SUBNET:{
+ struct iscsi_subnet_config *subnet =
+ entry->config.subnet;
+
+ if (subnet->address)
+ free(subnet->address);
+
+ free(subnet);
+ entry->config.subnet = NULL;
+ break;
+ }
+ default:
+ log_error("can't free unknown config entry %p type %u\n",
+ entry, entry->type);
+ break;
+ }
+
+ free(entry);
+}
+
+struct iscsi_auth_config *
+entry_auth_options(struct iscsi_config_entry *entry)
+{
+ if (entry == NULL)
+ return NULL;
+
+ switch (entry->type) {
+ case CONFIG_TYPE_SENDTARGETS:{
+ struct iscsi_sendtargets_config *sendtargets =
+ entry->config.sendtargets;
+
+ return &sendtargets->auth_options;
+ }
+ case CONFIG_TYPE_SLP:{
+ struct iscsi_slp_config *slp = entry->config.slp;
+
+ return &slp->auth_options;
+ }
+ case CONFIG_TYPE_DISCOVERY_FILE:{
+ struct iscsi_discovery_file_config *file =
+ entry->config.file;
+
+ return &file->auth_options;
+ }
+ case CONFIG_TYPE_TARGETNAME:{
+#ifdef PER_TARGETNAME_AUTH
+ struct iscsi_targetname_config *targetname =
+ entry->config.targetname;
+
+ return &targetname->auth_options;
+#else
+ /* disable configuring auth by TargetName for now,
+ * and always get the auth options at run-time
+ * from discovery
+ */
+ return NULL;
+#endif
+ }
+ case CONFIG_TYPE_SUBNET:{
+ return NULL;
+ }
+ default:
+ return NULL;
+ }
+}
+
+struct iscsi_connection_timeout_config *
+entry_connection_timeout_options(struct iscsi_config_entry *entry)
+{
+ if (entry == NULL)
+ return NULL;
+
+ switch (entry->type) {
+ case CONFIG_TYPE_SENDTARGETS:{
+ struct iscsi_sendtargets_config *sendtargets =
+ entry->config.sendtargets;
+
+ return &sendtargets->connection_timeout_options;
+ }
+ case CONFIG_TYPE_SLP:{
+ return NULL;
+ }
+ case CONFIG_TYPE_TARGETNAME:{
+ struct iscsi_targetname_config *targetname =
+ entry->config.targetname;
+
+ return &targetname->connection_timeout_options;
+ }
+ case CONFIG_TYPE_SUBNET:{
+ struct iscsi_subnet_config *subnet =
+ entry->config.subnet;
+
+ return &subnet->connection_timeout_options;
+ }
+ default:
+ return NULL;
+ }
+}
+
+struct iscsi_session_timeout_config *
+entry_session_timeout_options(struct iscsi_config_entry *entry)
+{
+ if (entry == NULL)
+ return NULL;
+
+ switch (entry->type) {
+ case CONFIG_TYPE_SENDTARGETS:{
+ return NULL;
+ }
+ case CONFIG_TYPE_SLP:{
+ return NULL;
+ }
+ case CONFIG_TYPE_TARGETNAME:{
+ struct iscsi_targetname_config *targetname =
+ entry->config.targetname;
+
+ return &targetname->session_timeout_options;
+ }
+ case CONFIG_TYPE_SUBNET:{
+ return NULL;
+ }
+ default:
+ return NULL;
+ }
+}
+
+struct iscsi_error_timeout_config *
+entry_error_timeout_options(struct iscsi_config_entry *entry)
+{
+ if (entry == NULL)
+ return NULL;
+
+ switch (entry->type) {
+ case CONFIG_TYPE_SENDTARGETS:{
+ return NULL;
+ }
+ case CONFIG_TYPE_SLP:{
+ return NULL;
+ }
+ case CONFIG_TYPE_TARGETNAME:{
+ struct iscsi_targetname_config *targetname =
+ entry->config.targetname;
+
+ return &targetname->error_timeout_options;
+ }
+ case CONFIG_TYPE_SUBNET:{
+ struct iscsi_subnet_config *subnet =
+ entry->config.subnet;
+
+ return &subnet->error_timeout_options;
+ }
+ default:
+ return NULL;
+ }
+}
+
+struct iscsi_tcp_config *
+entry_tcp_options(struct iscsi_config_entry *entry)
+{
+ if (entry == NULL)
+ return NULL;
+
+ switch (entry->type) {
+ case CONFIG_TYPE_SENDTARGETS:{
+ return NULL;
+ }
+ case CONFIG_TYPE_SLP:{
+ return NULL;
+ }
+ case CONFIG_TYPE_TARGETNAME:{
+ struct iscsi_targetname_config *targetname =
+ entry->config.targetname;
+
+ return &targetname->tcp_options;
+ }
+ case CONFIG_TYPE_SUBNET:{
+ struct iscsi_subnet_config *subnet =
+ entry->config.subnet;
+
+ return &subnet->tcp_options;
+ }
+ default:
+ return NULL;
+ }
+}
+
+struct iscsi_operational_config *
+entry_iscsi_options(struct iscsi_config_entry *entry)
+{
+ if (entry == NULL)
+ return NULL;
+
+ switch (entry->type) {
+ case CONFIG_TYPE_SENDTARGETS:{
+ return NULL;
+ }
+ case CONFIG_TYPE_SLP:{
+ return NULL;
+ }
+ case CONFIG_TYPE_TARGETNAME:{
+ struct iscsi_targetname_config *targetname =
+ entry->config.targetname;
+
+ return &targetname->iscsi_options;
+ }
+ case CONFIG_TYPE_SUBNET:{
+ return NULL;
+ }
+ default:
+ return NULL;
+ }
+}
+
+int
+same_portal_descriptor(struct iscsi_portal_descriptor *p1,
+ struct iscsi_portal_descriptor *p2)
+{
+ if (p1->port != p2->port)
+ return 0;
+
+ if (p1->tag != p2->tag)
+ return 0;
+
+ if (p1->ip_length != p2->ip_length)
+ return 0;
+
+ if (memcmp(p1->ip, p2->ip, p1->ip_length))
+ return 0;
+
+ return 1;
+}
+
+int
+same_portal_descriptors(struct iscsi_portal_descriptor *portals1,
+ struct iscsi_portal_descriptor *portals2)
+{
+ struct iscsi_portal_descriptor *p1, *p2;
+
+ if (portals1 && !portals2)
+ return 0;
+
+ if (!portals1 && portals2)
+ return 0;
+
+ /* same when p1 is a subset of p2 and p2 is a subset of p1 */
+
+ for (p1 = portals1; p1; p1 = p1->next) {
+ int same = 0;
+
+ for (p2 = portals2; p2; p2 = p2->next) {
+ if (same_portal_descriptor(p1, p2)) {
+ same = 1;
+ break;
+ }
+ }
+
+ if (!same)
+ return 0;
+ }
+
+ for (p2 = portals2; p2; p2 = p2->next) {
+ int same = 0;
+
+ for (p1 = portals1; p1; p1 = p1->next) {
+ if (same_portal_descriptor(p1, p2)) {
+ same = 1;
+ break;
+ }
+ }
+
+ if (!same)
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+same_portal_config(struct iscsi_portal_config *p1,
+ struct iscsi_portal_config *p2)
+{
+ /* Note: this needs to be updated whenever new structures are
+ * added to the portal config
+ */
+ if (memcmp
+ (&p1->connection_timeout_options, &p2->connection_timeout_options,
+ sizeof (p1->connection_timeout_options)))
+ return 0;
+
+ if (memcmp(&p1->session_timeout_options, &p2->session_timeout_options,
+ sizeof (p1->session_timeout_options)))
+ return 0;
+
+ if (memcmp(&p1->error_timeout_options, &p2->error_timeout_options,
+ sizeof (p1->error_timeout_options)))
+ return 0;
+
+ if (memcmp(&p1->tcp_options, &p2->tcp_options,
+ sizeof (p1->tcp_options)))
+ return 0;
+
+ if (memcmp(&p1->iscsi_options, &p2->iscsi_options,
+ sizeof (p1->iscsi_options)))
+ return 0;
+
+ if (!same_portal_descriptor(p1->descriptor, p2->descriptor))
+ return 0;
+
+ return 1;
+}
+
+int
+same_portal_configs(struct iscsi_portal_config *p1,
+ struct iscsi_portal_config *p2)
+{
+ /* FIXME: ordering currently matters, but perhaps shouldn't */
+ while (p1 || p2) {
+
+ if (p1 && !p2)
+ return 0;
+
+ if (!p1 && p2)
+ return 0;
+
+ if (!same_portal_config(p1, p2))
+ return 0;
+
+ p1 = p1->next;
+ p2 = p2->next;
+ }
+
+ return 1;
+}
+
+int
+same_session_config(struct iscsi_session_config *s1,
+ struct iscsi_session_config *s2)
+{
+ /* Note: this needs to be updated whenever fields are
+ * added to struct iscsi_session_config
+ */
+
+ if (s1->iscsi_bus != s2->iscsi_bus)
+ return 0;
+
+ if (s1->target_id != s2->target_id)
+ return 0;
+
+ if (memcmp(s1->isid, s2->isid, sizeof (s1->isid)))
+ return 0;
+
+ if (s1->path_number != s2->path_number)
+ return 0;
+
+ if (!same_portal_config(s1->portal, s2->portal))
+ return 0;
+
+ return 1;
+}
+
+int
+same_target_config(struct iscsi_target_config *t1,
+ struct iscsi_target_config *t2)
+{
+ /* Note: this needs to be updated whenever fields are
+ * added to struct iscsi_target_config
+ */
+
+ if (!t1 && !t2)
+ return 1;
+
+ if (t1 && !t2)
+ return 0;
+
+ if (!t1 && t2)
+ return 0;
+
+ if (t1->enabled != t2->enabled)
+ return 0;
+
+ if (memcmp
+ (&t1->auth_options, &t2->auth_options, sizeof (t1->auth_options)))
+ return 0;
+
+ /* we don't recursively compare session configs */
+
+ return 1;
+}
+
+void
+free_portal_descriptors(struct iscsi_portal_descriptor *portals)
+{
+ struct iscsi_portal_descriptor *portal;
+
+ while ((portal = portals)) {
+ portals = portal->next;
+ if (portal->address) {
+ log_debug(6, "freeing portal descriptor address %p",
+ portal->address);
+ free(portal->address);
+ }
+ log_debug(6, "freeing portal descriptor %p", portal);
+ free(portal);
+ }
+}
+
+void
+free_session_config(struct iscsi_session_config *config)
+{
+
+ /* free the portal config */
+
+ /*
+ * we assume something else (the daemon's struct iscsi_target)
+ * is managing the lifetime of the descriptors, so we
+ * don't free them here.
+ */
+
+ log_debug(6, "freeing portal config %p of session config %p",
+ config->portal, config);
+ free(config->portal);
+
+ /* and the config itself */
+ log_debug(6, "freeing session config %p of target config %p", config,
+ config->target);
+ free(config);
+}
+
+void
+free_target_config(struct iscsi_target_config *config)
+{
+ struct iscsi_session_config *session;
+
+ /* free the session configs */
+ while ((session = config->sessions)) {
+ config->sessions = session->next;
+
+ free_session_config(session);
+ }
+
+ /* we assume something else (the daemon's struct iscsi_target)
+ * is managing the lifetime of the TargetName, so we dont
+ * free it here.
+ */
+ config->TargetName = NULL;
+
+ /* don't leave passwords in memory */
+ memset(&config->auth_options, 0, sizeof (config->auth_options));
+
+ /* free the config itself */
+ log_debug(6, "freeing target config %p", config);
+ free(config);
+}
+
+int
+parse_boolean(char *str)
+{
+ char *end = str;
+ int value = -1;
+ int c;
+
+ /* stop at the first whitespace */
+ while (*end && !isspace(c = *end))
+ end++;
+ *end = '\0';
+
+ if (strncasecmp(str, "yes", 3) == 0) {
+ value = 1;
+ } else if (strncasecmp(str, "no", 2) == 0) {
+ value = 0;
+ } else {
+ value = strtol(str, &end, 0);
+ /* check for invalid input */
+ if (*str && (*end != '\0'))
+ value = -1;
+ }
+
+ return value;
+}
+
+int
+parse_number(char *str)
+{
+ char *end = str;
+ int number = -1;
+ int c;
+
+ /* stop at the first whitespace */
+ while (*end && !isspace(c = *end))
+ end++;
+ *end = '\0';
+
+ number = strtol(str, &end, 0);
+
+ if (*str && (*end == '\0'))
+ return number; /* it was all valid */
+ else
+ return -1; /* something was invalid */
+}
+
+/* FIXME: accept suffixes for seconds, minutes, hours */
+int
+parse_time(char *str)
+{
+ char *end = str;
+ int number = -1;
+ int c;
+ int units = 1;
+
+ /* stop at the first whitespace */
+ while (*end && !isspace(c = *end))
+ end++;
+ *end = '\0';
+
+ end--;
+ switch (*end) {
+ case 's':
+ units = 1; /* seconds */
+ *end = '\0';
+ break;
+ case 'm':
+ units = 60; /* minutes */
+ *end = '\0';
+ break;
+ case 'h':
+ units = 60 * 60; /* hours */
+ *end = '\0';
+ break;
+ default:
+ /* let strtol flag it as invalid */
+ break;
+ }
+ end++;
+
+ /* FIXME: check for overflow */
+ number = strtol(str, &end, 0) * units;
+
+ if (*str && (*end == '\0'))
+ return number; /* it was all valid */
+ else
+ return -1; /* something was invalid */
+}
+
+/*
+ * If string is quoted, terminate it at the end quote
+ * - else terminate at first whitespace.
+ * Return pointer after leading quote.
+ */
+char *
+parse_quoted_string(char *strp)
+{
+ char *cp, *retp = strp;
+ int c;
+
+ if (*strp == '"') {
+ cp = ++strp;
+ retp = cp;
+ /* find the end quote and NUL it */
+ while ((*cp != '\0') && (*cp != '"')) {
+ cp++;
+ }
+ *cp = '\0';
+ } else {
+ /* not quoted - terminate it at first whitespace */
+ cp = strp;
+ while ((*cp != '\0') && (!isspace(c = *cp))) {
+ cp++;
+ }
+ *cp = '\0';
+ }
+
+ return retp;
+}
+
+/*
+ * update the existing config so that it matches
+ * what's currently in the config file
+ * FIXME: use a real parser.
+ */
+int
+update_iscsi_config(const char *pathname, struct iscsi_config *config)
+{
+ FILE *f = NULL;
+ char *line, *nl, buffer[2048];
+ int c;
+ struct iscsi_config_entry *entry = NULL, *current_entry =
+ NULL, *slp_entry = NULL;
+ int indent = 0, entry_indent = 0;
+ int line_number = 1;
+ int slp_multicast_seen = 0;
+
+ if (!config)
+ return 0;
+
+ f = fopen(pathname, "r");
+ if (!f) {
+ log_error("Cannot open configuration file %s", pathname);
+ return 0;
+ }
+
+ log_debug(5, "updating config %p from %s", config, pathname);
+
+ /* clear out any existing config */
+ while ((entry = config->head)) {
+ remove_config_entry(config, entry);
+ free_config_entry(entry);
+ }
+
+
+ memset(config, 0, sizeof (*config));
+ config->head = config->tail = NULL;
+
+ /* reset to the platform's usual defaults */
+ iscsi_init_config_defaults(&config->defaults);
+
+ /* process the config file */
+ do {
+ line = fgets(buffer, sizeof (buffer), f);
+ line_number++;
+ if (!line)
+ continue;
+
+ /* skip but record leading whitespace */
+ indent = 0;
+ while (isspace(c = *line)) {
+ if (*line == '\t')
+ indent += 8;
+ else
+ indent++;
+
+ line++;
+ }
+
+ /* strip trailing whitespace, including the newline.
+ * anything that needs the whitespace must be quoted.
+ */
+ nl = line + strlen(line) - 1;
+ if (*nl == '\n') {
+ do {
+ *nl = '\0';
+ nl--;
+ } while (isspace(c = *nl));
+ } else {
+ log_error("config file line %d too long",
+ line_number);
+ return 0;
+ }
+
+ /* process any non-empty, non-comment lines */
+ if (*line && (*line != '#')) {
+
+ log_debug(7, "config indent %d, line %s", indent, line);
+
+ /* if this line isn't indented farther than
+ * the current entry, it's unrelated.
+ */
+ if (indent <= entry_indent)
+ current_entry = NULL;
+
+ if ((strncasecmp(line, "TargetIpAddr=", 13) == 0) ||
+ (strncasecmp(line, "DiscoveryAddress=", 17) == 0)) {
+ char *addr = NULL, *port = NULL;
+ char *sep = line;
+ struct iscsi_sendtargets_config
+ *sendtargets_config = NULL;
+
+ /* find the start of the address */
+ while (*sep && *sep != '=')
+ sep++;
+ addr = ++sep;
+ if (*addr) {
+ /* look for a port number */
+ /* FIXME: ought to handle the
+ * IPv6 syntax in the iSCSI spec
+ */
+ while (*sep && !isspace(c = *sep)
+ && *sep != ':')
+ sep++;
+ if (*sep == ':') {
+ *sep = '\0';
+ port = ++sep;
+ while (*sep
+ && isdigit(c =
+ *sep))
+ sep++;
+ *sep = '\0';
+ } else
+ *sep = '\0';
+
+ /* create a new sendtargets config entry
+ */
+ entry = calloc(1, sizeof (*entry));
+ if (entry == NULL) {
+ log_error(
+ "failed to allocate "
+ "config entry");
+ return 0;
+ }
+ entry->line_number = line_number;
+
+ sendtargets_config =
+ calloc(1,
+ sizeof
+ (*sendtargets_config));
+ if (sendtargets_config == NULL) {
+ free(entry);
+ entry = NULL;
+ log_error(
+ "failed to allocate "
+ "sendtargets config");
+ return 0;
+ }
+
+ entry->type = CONFIG_TYPE_SENDTARGETS;
+ entry->config.sendtargets =
+ sendtargets_config;
+
+ /* capture the current global defaults
+ */
+ memcpy(&sendtargets_config->
+ auth_options,
+ &config->defaults.auth_options,
+ sizeof (sendtargets_config->
+ auth_options));
+ memcpy(&sendtargets_config->
+ connection_timeout_options,
+ &config->defaults.
+ connection_timeout_options,
+ sizeof (sendtargets_config->
+ connection_timeout_options));
+ sendtargets_config->continuous =
+ config->defaults.
+ continuous_sendtargets;
+ sendtargets_config->send_async_text =
+ config->defaults.send_async_text;
+
+ /* record the address and port */
+ sendtargets_config->address =
+ strdup(addr);
+ if (port && *port
+ && atoi(port) > 0)
+ sendtargets_config->port =
+ atoi(port);
+ else
+ sendtargets_config->port =
+ ISCSI_DEFAULT_PORT;
+
+ /* append it to the list of all
+ * config entries
+ */
+ add_config_entry(config, entry);
+ log_debug(5,
+ "config entry %p "
+ "sendtargets %p = %s:%d",
+ entry, sendtargets_config,
+ addr,
+ sendtargets_config->port);
+
+ /* indented settings in the config file
+ * may modify this entry
+ */
+ current_entry = entry;
+ entry_indent = indent;
+ } else {
+ log_error(
+ "error on line %d of %s, an "
+ "address is required",
+ line_number, pathname);
+ }
+ } else if (strncasecmp(line, "DiscoveryFile=", 14) == 0) {
+ char *filename = line + 14;
+ struct iscsi_discovery_file_config *file_config;
+
+ if (strlen(filename)) {
+ /* create a new sendtargets config entry */
+ entry = calloc(1, sizeof (*entry));
+ if (entry == NULL) {
+ log_error(
+ "failed to allocate "
+ "config entry");
+ return 0;
+ }
+ entry->line_number = line_number;
+
+ file_config =
+ calloc(1, sizeof (*file_config));
+ if (file_config == NULL) {
+ free(entry);
+ entry = NULL;
+ log_error(
+ "failed to allocate "
+ "discovery file config");
+ return 0;
+ }
+
+ entry->type =
+ CONFIG_TYPE_DISCOVERY_FILE;
+ entry->config.file = file_config;
+
+ /* capture the current global defaults
+ */
+ file_config->read_size = 512;
+ /* FIXME: make this configurable */
+ file_config->continuous =
+ config->defaults.
+ continuous_sendtargets;
+ memcpy(&file_config->auth_options,
+ &config->defaults.auth_options,
+ sizeof (file_config->
+ auth_options));
+
+ /* record the filename */
+ file_config->filename =
+ strdup(filename);
+
+ /* append it to the list of all
+ * config entries
+ */
+ add_config_entry(config, entry);
+ log_debug(5,
+ "config entry %p discovery "
+ "file %p = %s",
+ entry, file_config, filename);
+
+ /* indented settings in the config file
+ * may modify this entry
+ */
+ current_entry = entry;
+ entry_indent = indent;
+ } else {
+ log_error(
+ "error on line %d of %s, "
+ "DiscoveryFile entry requires "
+ "a filename",
+ line_number, pathname);
+ }
+ } else if (strncasecmp(line, "Continuous=", 11) == 0) {
+ int value = parse_boolean(line + 11);
+
+ if (value < 0) {
+ log_error(
+ "error on line %d of %s, "
+ "invalid value %s",
+ line_number, pathname,
+ line + 11);
+ } else if (current_entry
+ && current_entry->type ==
+ CONFIG_TYPE_SENDTARGETS) {
+ struct iscsi_sendtargets_config
+ *sendtargets =
+ current_entry->config.sendtargets;
+
+ sendtargets->continuous = value;
+ log_debug(5,
+ "config entry %p sendtargets "
+ "config %p continuous %d",
+ current_entry, sendtargets,
+ value);
+ } else if (current_entry
+ && current_entry->type ==
+ CONFIG_TYPE_DISCOVERY_FILE) {
+ struct iscsi_discovery_file_config
+ *file_config =
+ current_entry->config.file;
+
+ file_config->continuous = value;
+ log_debug(5,
+ "config entry %p discovery "
+ "file config %p continuous %d",
+ current_entry, file_config,
+ value);
+ } else {
+ config->defaults.
+ continuous_sendtargets = value;
+ log_debug(5,
+ "config global continuous "
+ "discovery %d",
+ value);
+ }
+ } else if (strncasecmp(line, "SendAsyncText=", 14) == 0) {
+ char *str = &line[14];
+ int value = parse_boolean(str);
+
+ if (value >= 0) {
+ if (current_entry
+ && current_entry->type ==
+ CONFIG_TYPE_SENDTARGETS) {
+ struct iscsi_sendtargets_config
+ *sendtargets =
+ current_entry->config.
+ sendtargets;
+ sendtargets->send_async_text =
+ value;
+ log_debug(5,
+ "config entry %p "
+ "sendtargets config%p "
+ "sendasynctext %d",
+ current_entry,
+ sendtargets, value);
+ } else {
+ config->defaults.
+ send_async_text = value;
+ log_debug(5,
+ "config global "
+ "SendAsyncText "
+ "value %d",
+ value);
+ }
+ } else {
+ log_error(
+ "error on line %d of %s, "
+ "invalid value %s",
+ line_number, pathname,
+ line + 14);
+ }
+ } else if (strncasecmp(line, "ReadSize=", 9) == 0) {
+ int value = parse_number(line + 9);
+
+ /* for testing, allow variable read sizes
+ * to simulate varying PDU sizes
+ */
+ if (value < 0) {
+ log_error(
+ "error on line %d of %s, "
+ "invalid entry %s",
+ line_number, pathname, line);
+ } else if (current_entry
+ && current_entry->type ==
+ CONFIG_TYPE_DISCOVERY_FILE) {
+ struct iscsi_discovery_file_config
+ *file_config =
+ current_entry->config.file;
+
+ file_config->read_size = value;
+ log_debug(5,
+ "config entry %p discovery "
+ "file config %p continuous %d",
+ current_entry, file_config,
+ value);
+ } else {
+ log_error(
+ "error on line %d of %s, "
+ "invalid entry %s",
+ line_number, pathname, line);
+ }
+ } else if (strncasecmp(line, "DefaultAddress=", 15) ==
+ 0) {
+ /* allow DiscoveryFile entries to specify
+ * a default address, so that TargetAddresses
+ * can be omitted from the discovery file.
+ */
+ if (current_entry
+ && current_entry->type ==
+ CONFIG_TYPE_DISCOVERY_FILE) {
+ struct iscsi_discovery_file_config
+ *file_config =
+ current_entry->config.file;
+ char *address = line + 15;
+ char *port = NULL;
+
+ if ((port = strrchr(address, ':'))) {
+ *port = '\0';
+ port++;
+ file_config->port =
+ strdup(port);
+ } else {
+ file_config->port =
+ strdup("3260");
+ }
+
+ file_config->address = strdup(address);
+ log_debug(5,
+ "config entry %p discovery "
+ "file config %p address %s "
+ "port %s ",
+ current_entry, file_config,
+ file_config->address,
+ file_config->port);
+ } else {
+ log_error(
+ "error on line %d of %s, "
+ "invalid entry %s",
+ line_number, pathname, line);
+ }
+ } else if (strncasecmp(line,"SLPMulticast=",13) == 0) {
+ char *value = parse_quoted_string(line + 13);
+ struct iscsi_slp_config *slp_config;
+
+ slp_multicast_seen = 1;
+
+ if ((value == NULL) || (*value == '\0')) {
+ log_error(
+ "error on line %d of %s, "
+ "SLPMulticast requires a list "
+ "of interface names\n",
+ line_number, pathname);
+ continue;
+ }
+
+ /* add an entry for SLP */
+ entry = calloc(1, sizeof (*entry));
+ if (entry == NULL) {
+ log_error(
+ "failed to allocate "
+ "config entry");
+ return 0;
+ }
+ entry->line_number = line_number;
+
+ slp_config =
+ calloc(1, sizeof (struct iscsi_slp_config));
+ if (slp_config == NULL) {
+ log_error(
+ "failed to allocate SLP config");
+ return 0;
+ }
+
+ entry->type = CONFIG_TYPE_SLP;
+ entry->config.slp = slp_config;
+
+ slp_entry = entry;
+
+ /* capture the global defaults */
+ memcpy(&slp_config->auth_options,
+ &config->defaults.auth_options,
+ sizeof (slp_config->auth_options));
+ if (config->defaults.slp_scopes)
+ slp_config->scopes =
+ strdup(config->defaults.slp_scopes);
+ else
+ slp_config->scopes = NULL;
+ slp_config->poll_interval =
+ config->defaults.slp_poll_interval;
+
+ /* multicast on the specified interfaces */
+ slp_config->interfaces = strdup(value);
+ slp_config->address = NULL;
+ slp_config->port = 0;
+
+ /* append it to the list of all config entries
+ */
+ add_config_entry(config, entry);
+ log_debug(5,
+ "config entry %p SLPMulticast %p = %s",
+ entry, slp_config, value);
+
+ /* indented settings in the config file
+ * may modify this entry
+ */
+ current_entry = entry;
+ entry_indent = indent;
+ } else if (strncasecmp(line, "SLPUnicast=", 11) == 0) {
+ char *addr = NULL, *port = NULL;
+ char *sep = line;
+ struct iscsi_slp_config *slp_config = NULL;
+
+ /* find the start of the address */
+ while (*sep && *sep != '=')
+ sep++;
+
+ addr = ++sep;
+ if (*addr) {
+ /* look for a port number */
+ /* FIXME: ought to handle the IPv6
+ * syntax in the iSCSI spec
+ */
+ while (*sep && !isspace(c = *sep)
+ && *sep != ':')
+ sep++;
+ if (*sep == ':') {
+ *sep = '\0';
+ port = ++sep;
+ while (*sep
+ && isdigit(c = *sep))
+ sep++;
+ *sep = '\0';
+ } else
+ *sep = '\0';
+
+ /* create a new slp config entry */
+ entry = calloc(1, sizeof (*entry));
+ if (entry == NULL) {
+ log_error(
+ "failed to allocate "
+ "config entry");
+ return 0;
+ }
+ entry->line_number = line_number;
+
+ slp_config =
+ calloc(1, sizeof (*slp_config));
+ if (slp_config == NULL) {
+ free(entry);
+ entry = NULL;
+ log_error(
+ "failed to allocate "
+ "SLP config");
+ return 0;
+ }
+
+ entry->type = CONFIG_TYPE_SLP;
+ entry->config.slp = slp_config;
+
+ /* capture the current global defaults
+ */
+ memcpy(&slp_config->auth_options,
+ &config->defaults.auth_options,
+ sizeof (slp_config->
+ auth_options));
+ if (config->defaults.slp_scopes)
+ slp_config->scopes =
+ strdup(config->defaults.
+ slp_scopes);
+ else
+ slp_config->scopes = NULL;
+ slp_config->poll_interval =
+ config->defaults.slp_poll_interval;
+
+ /* record the address and port */
+ slp_config->address = strdup(addr);
+ if (port && *port && (atoi(port) > 0)) /* FIXME: check for invalid port strings */
+ slp_config->port =
+ atoi(port);
+ else
+ slp_config->port = ISCSI_DEFAULT_PORT; /* FIXME: what is the default SLP port? */
+
+ slp_config->interfaces = NULL;
+ /* let the OS pick an interface
+ * for unicasts
+ */
+
+ /* append it to the list of all
+ * config entries
+ */
+ add_config_entry(config, entry);
+ log_debug(5,
+ "config entry %p SLPUnicast "
+ "%p = %s:%d",
+ entry, slp_config, addr,
+ slp_config->port);
+
+ /* indented settings in the config file
+ * may modify this entry
+ */
+ current_entry = entry;
+ entry_indent = indent;
+ } else {
+ log_error(
+ "error on line %d of %s, an "
+ "address is required",
+ line_number, pathname);
+ }
+ } else if (strncasecmp(line, "PollInterval=", 13) == 0) {
+ int value = parse_time(line + 13);
+
+ if (value < 0) {
+ log_error(
+ "error on line %d of %s, "
+ "illegal value %s",
+ line_number, pathname,
+ line + 13);
+ } else if (current_entry
+ && current_entry->type ==
+ CONFIG_TYPE_SLP) {
+ struct iscsi_slp_config *slp =
+ current_entry->config.slp;
+
+ slp->poll_interval = value;
+ log_debug(5,
+ "config entry %p poll "
+ "interval %d",
+ slp, value);
+ } else {
+ config->defaults.slp_poll_interval =
+ value;
+ log_debug(5,
+ "config global SLP "
+ "poll interval %d",
+ value);
+ }
+ } else if (strncasecmp(line, "TargetName=", 11) == 0) {
+ /* settings for a specific iSCSI TargetName
+ * (can be quoted)
+ */
+ char *n = parse_quoted_string(line + 11);
+ struct iscsi_targetname_config
+ *targetname_config;
+
+ entry = calloc(1, sizeof (*entry));
+ if (entry == NULL) {
+ log_error(
+ "failed to allocate "
+ "config entry");
+ return 0;
+ }
+ entry->line_number = line_number;
+
+ targetname_config =
+ calloc(1, sizeof (*targetname_config));
+ if (targetname_config == NULL) {
+ free(entry);
+ entry = NULL;
+ log_error(
+ "failed to allocate "
+ "TargetName config");
+ return 0;
+ }
+
+ entry->type = CONFIG_TYPE_TARGETNAME;
+ entry->config.targetname = targetname_config;
+
+ targetname_config->TargetName = strdup(n);
+
+ /* capture the current global defaults */
+ targetname_config->enabled =
+ config->defaults.enabled;
+
+ memcpy(&targetname_config->auth_options,
+ &config->defaults.auth_options,
+ sizeof (targetname_config->
+ auth_options));
+
+ memcpy(&targetname_config->
+ connection_timeout_options,
+ &config->defaults.
+ connection_timeout_options,
+ sizeof (targetname_config->
+ connection_timeout_options));
+ memcpy(&targetname_config->
+ session_timeout_options,
+ &config->defaults.
+ session_timeout_options,
+ sizeof (targetname_config->
+ session_timeout_options));
+ memcpy(&targetname_config->
+ error_timeout_options,
+ &config->defaults.error_timeout_options,
+ sizeof (targetname_config->
+ error_timeout_options));
+ memcpy(&targetname_config->tcp_options,
+ &config->defaults.tcp_options,
+ sizeof (targetname_config->tcp_options));
+ memcpy(&targetname_config->iscsi_options,
+ &config->defaults.iscsi_options,
+ sizeof (targetname_config->
+ iscsi_options));
+
+ /* append it to the list of all config entries
+ */
+ add_config_entry(config, entry);
+ log_debug(5,
+ "config entry %p targetname %p = %s",
+ entry, targetname_config, n);
+
+ /* indented settings in the config file
+ * may modify this entry
+ */
+ current_entry = entry;
+ entry_indent = indent;
+ } else if (strncasecmp(line, "Enabled=", 8) == 0) {
+ char *str = &line[8];
+ int value = parse_boolean(str);
+
+ if (value >= 0) {
+ if (current_entry) {
+ if (current_entry->type ==
+ CONFIG_TYPE_TARGETNAME) {
+ struct iscsi_targetname_config
+ *targetname_config =
+ current_entry->
+ config.targetname;
+
+ targetname_config->
+ enabled = value;
+ log_debug(5,
+ "config entry "
+ "%p targetname"
+ " %p to %s %s",
+ current_entry,
+ targetname_config,
+ targetname_config->
+ TargetName,
+ value ?
+ "enabled" :
+ "disabled");
+ } else {
+ log_error(
+ "error on line "
+ "%d of %s, "
+ "enabled does "
+ "not apply to "
+ "the current "
+ "entry",
+ line_number,
+ pathname);
+ }
+ } else {
+ log_debug(5,
+ "config global "
+ "targets %s",
+ value ? "enabled" :
+ "disabled");
+ config->defaults.enabled =
+ value;
+ }
+ } else
+ log_error(
+ "error on line %d of %s, "
+ "illegal value %s",
+ line_number, pathname, str);
+ } else if ((strncasecmp(line, "Username=", 9) == 0) ||
+ (strncasecmp(line, "UsernameOut=", 12) == 0) ||
+ (strncasecmp(line, "OutgoingUsername=", 17) ==
+ 0)) {
+ /* Username string can be quoted */
+ char *u, *cp = &line[8];
+
+ /* find start of value */
+ while (*cp && *cp != '=') {
+ cp++;
+ }
+ u = parse_quoted_string(++cp);
+
+ if (current_entry) {
+ struct iscsi_auth_config *auth_options =
+ entry_auth_options(current_entry);
+ if (auth_options) {
+ strncpy(auth_options->username,
+ u,
+ sizeof (auth_options->
+ username));
+ auth_options->
+ username[sizeof
+ (auth_options->
+ username) - 1] =
+ '\0';
+ log_debug(5,
+ "config entry %p "
+ "outgoing username %s",
+ current_entry,
+ auth_options->
+ username);
+ } else {
+ log_error(
+ "Invalid entry on line"
+ " %d of %s,"
+ " current entry cannot "
+ "have an outgoing "
+ "username, see man "
+ "page of iscsi.conf",
+ line_number, pathname);
+ }
+ } else {
+ /* default to use while processing the
+ * rest of the config file
+ */
+ strncpy(config->defaults.auth_options.
+ username, u,
+ sizeof (config->defaults.
+ auth_options.username));
+ config->defaults.auth_options.
+ username[sizeof
+ (config->defaults.
+ auth_options.username) -
+ 1] = '\0';
+ log_debug(5,
+ "config global outgoing "
+ "username %s",
+ config->defaults.auth_options.
+ username);
+ }
+ } else if ((strncasecmp(line, "UsernameIn=", 11) == 0)
+ ||
+ (strncasecmp(line, "IncomingUsername=", 17)
+ == 0)) {
+ char *u, *cp = line + 10;
+
+ /* find start of value */
+ while (*cp && *cp != '=') {
+ cp++;
+ }
+ u = parse_quoted_string(++cp);
+
+ if (current_entry) {
+ struct iscsi_auth_config *auth_options =
+ entry_auth_options(current_entry);
+ if (auth_options) {
+ strncpy(auth_options->
+ username_in, u,
+ sizeof (auth_options->
+ username_in));
+ auth_options->
+ username_in[sizeof
+ (auth_options->
+ username_in) -
+ 1] = '\0';
+ log_debug(5,
+ "config entry %p "
+ "incoming username %s",
+ current_entry,
+ auth_options->
+ username_in);
+ } else {
+ log_error(
+ "Invalid entry on line"
+ " %d of %s,"
+ " current entry cannot "
+ "have an incoming "
+ "username, see "
+ "man page of iscsi."
+ "conf",
+ line_number, pathname);
+ }
+ } else {
+ /* default to use while processing the
+ * rest of the config file
+ */
+ strncpy(config->defaults.auth_options.
+ username_in, u,
+ sizeof (config->defaults.
+ auth_options.
+ username_in));
+ config->defaults.auth_options.
+ username_in[sizeof
+ (config->defaults.
+ auth_options.
+ username_in) - 1] =
+ '\0';
+ log_debug(5,
+ "config global incoming "
+ "username %s",
+ config->defaults.auth_options.
+ username_in);
+ }
+ } else if ((strncasecmp(line, "Password=", 9) == 0) ||
+ (strncasecmp(line, "PasswordOut=", 12) == 0)
+ ||
+ (strncasecmp(line, "OutgoingPassword=", 17)
+ == 0)) {
+ /* Password string can be quoted */
+ char *p, *cp = &line[8];
+
+ /* find start of value */
+ while (*cp && *cp != '=') {
+ cp++;
+ }
+ p = parse_quoted_string(++cp);
+
+ if (current_entry) {
+ struct iscsi_auth_config *auth_options =
+ entry_auth_options(current_entry);
+ if (auth_options) {
+ strncpy(auth_options->password,
+ p,
+ sizeof (auth_options->
+ password));
+ auth_options->
+ password[sizeof
+ (auth_options->
+ password) - 1] =
+ '\0';
+ auth_options->password_length =
+ strlen(auth_options->
+ password);
+ log_debug(5,
+ "config entry %p "
+ "outgoing password %s "
+ "length %u",
+ current_entry,
+ auth_options->password,
+ auth_options->
+ password_length);
+ } else {
+ log_error(
+ "Invalid entry on line "
+ "%d of %s,"
+ " current entry cannot "
+ "have an outgoing "
+ "password, see "
+ "man page of iscsi."
+ "conf",
+ line_number, pathname);
+ }
+ } else {
+ /* default to use while processing the
+ * rest of the config file
+ */
+ strncpy(config->defaults.auth_options.
+ password, p,
+ sizeof (config->defaults.
+ auth_options.password));
+ config->defaults.auth_options.
+ password[sizeof
+ (config->defaults.
+ auth_options.password) -
+ 1] = '\0';
+ config->defaults.auth_options.
+ password_length =
+ strlen(config->defaults.
+ auth_options.password);
+ log_debug(5,
+ "config global outgoing "
+ "password %s length %u",
+ config->defaults.auth_options.
+ password,
+ config->defaults.auth_options.
+ password_length);
+ }
+ } else if ((strncasecmp(line, "PasswordIn=", 11) == 0)
+ ||
+ (strncasecmp(line, "IncomingPassword=", 17)
+ == 0)) {
+ char *p, *cp = line + 10;
+
+ /* find start of value */
+ while (*cp && *cp != '=') {
+ cp++;
+ }
+ p = parse_quoted_string(++cp);
+
+ if (current_entry) {
+ struct iscsi_auth_config *auth_options =
+ entry_auth_options(current_entry);
+ if (auth_options) {
+ strncpy(auth_options->
+ password_in, p,
+ sizeof (auth_options->
+ password_in));
+ auth_options->
+ password_in[sizeof
+ (auth_options->
+ password_in) -
+ 1] = '\0';
+ auth_options->
+ password_length_in =
+ strlen(auth_options->
+ password_in);
+ log_debug(5,
+ "config entry %p "
+ "incoming password %s,"
+ " length %u",
+ current_entry,
+ auth_options->
+ password_in,
+ auth_options->
+ password_length_in);
+ } else {
+ log_error(
+ "Invalid entry on line "
+ "%d of %s,"
+ " current entry cannot "
+ "have an incoming "
+ "password, see "
+ "man page of iscsi."
+ "conf",
+ line_number, pathname);
+ }
+ } else {
+ /* default to use while processing the
+ * rest of the config file
+ */
+ strncpy(config->defaults.auth_options.
+ password_in, p,
+ sizeof (config->defaults.
+ auth_options.
+ password_in));
+ config->defaults.auth_options.
+ password_in[sizeof
+ (config->defaults.
+ auth_options.
+ password_in) - 1] =
+ '\0';
+ config->defaults.auth_options.
+ password_length_in =
+ strlen(config->defaults.
+ auth_options.password_in);
+ log_debug(1,
+ "config global incoming "
+ "password %s, length %u",
+ config->defaults.auth_options.
+ password_in,
+ config->defaults.auth_options.
+ password_length_in);
+ }
+ } else if ((strncasecmp(line, "Subnet=", 7) == 0)
+ || (strncasecmp(line, "Address=", 8) == 0)) {
+ char *address = line + 6;
+ char *mask;
+ struct in_addr addr;
+
+ struct iscsi_subnet_config *subnet_config;
+
+ while (*address && (*address != '='))
+ address++;
+
+ address++;
+
+ entry = calloc(1, sizeof (*entry));
+ if (entry == NULL) {
+ log_error(
+ "failed to allocate "
+ "config entry");
+ return 0;
+ }
+ entry->line_number = line_number;
+
+ subnet_config =
+ calloc(1, sizeof (*subnet_config));
+ if (subnet_config == NULL) {
+ free(entry);
+ entry = NULL;
+ log_error(
+ "failed to allocate "
+ "Subnet config");
+ return 0;
+ }
+
+ entry->type = CONFIG_TYPE_SUBNET;
+ entry->config.subnet = subnet_config;
+
+ subnet_config->subnet_mask = 0xFFFFFFFFU;
+
+ /* look for a subnet mask */
+ if ((mask = strrchr(address, '/'))) {
+ int bits;
+
+ *mask = '\0'; /* terminate the address */
+ mask++; /* and calculate the mask */
+ bits = atoi(mask);
+ if ((bits >= 0) && (bits < 32))
+ subnet_config->subnet_mask =
+ 0xFFFFFFFFU << (32 - bits);
+ } else if ((mask = strrchr(address, '&'))) {
+ *mask = '\0'; /* terminate the address */
+ mask++; /* and calculate the mask */
+ subnet_config->subnet_mask =
+ (uint32_t) strtoul(mask, NULL, 16);
+ }
+
+ subnet_config->address = strdup(address);
+
+ /* FIXME: IPv6 */
+ if (inet_aton(address, &addr)) {
+ subnet_config->ip_length = 4;
+ memcpy(subnet_config->ip_address,
+ &addr.s_addr, 4);
+ } else {
+ /* discard this entry */
+ log_error(
+ "error on line %d of %s, "
+ "bogus Subnet address %s\n",
+ line_number, pathname, address);
+ free_config_entry(entry);
+ entry = NULL;
+ continue;
+ }
+
+ /* capture the current global defaults */
+ memcpy(&subnet_config->
+ connection_timeout_options,
+ &config->defaults.
+ connection_timeout_options,
+ sizeof (subnet_config->
+ connection_timeout_options));
+ memcpy(&subnet_config->error_timeout_options,
+ &config->defaults.error_timeout_options,
+ sizeof (subnet_config->
+ error_timeout_options));
+ memcpy(&subnet_config->tcp_options,
+ &config->defaults.tcp_options,
+ sizeof (subnet_config->tcp_options));
+
+ /* append it to the list of all config entries
+ */
+ add_config_entry(config, entry);
+ log_debug(5,
+ "config entry %p subnet %p = addr %s "
+ "ip %u.%u.%u.%u mask 0x%x\n",
+ entry, subnet_config, address,
+ subnet_config->ip_address[0],
+ subnet_config->ip_address[1],
+ subnet_config->ip_address[2],
+ subnet_config->ip_address[3],
+ subnet_config->subnet_mask);
+
+ /* indented settings in the config file may
+ * modify this entry
+ */
+ current_entry = entry;
+ entry_indent = indent;
+ } else if (strncasecmp(line, "TCPWindowSize=", 14) == 0) {
+ char *num = &line[14];
+ int value = parse_number(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_tcp_config
+ *tcp_options =
+ entry_tcp_options
+ (current_entry);
+
+ if (tcp_options) {
+ tcp_options->
+ window_size = value;
+ log_debug(5,
+ "config entry "
+ "%p "
+ "TCPWindowSize"
+ " %d",
+ current_entry,
+ value);
+ } else {
+ log_error(
+ "Invalid entry "
+ "on line "
+ "%d of %s, "
+ "TCPWindowSize "
+ "does not apply "
+ "to the current "
+ "entry, see "
+ "man page of"
+ "iscsi.conf",
+ line_number,
+ pathname);
+ }
+ } else {
+ config->defaults.tcp_options.
+ window_size = value;
+ log_debug(5,
+ "config global "
+ "TCPWindowSize %d",
+ value);
+ }
+ } else
+ log_error(
+ "error on line %d, "
+ "invalid TCPWindowSize %s",
+ line_number, num);
+ } else if (strncasecmp(line, "InitialR2T=", 11) == 0) {
+ char *str = &line[11];
+ int value = parse_boolean(str);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_operational_config
+ *iscsi_options =
+ entry_iscsi_options
+ (current_entry);
+
+ if (iscsi_options) {
+ log_debug(5,
+ "config entry "
+ "%p InitialR2T"
+ " %d",
+ current_entry,
+ value);
+ iscsi_options->
+ InitialR2T = value;
+ } else {
+ log_error(
+ "Invalid "
+ "InitialR2T"
+ " entry on "
+ "line "
+ "%d of %s, "
+ "see man page "
+ "of iscsi."
+ "conf",
+ line_number,
+ pathname);
+ }
+ } else {
+ log_debug(5,
+ "config global "
+ "InitialR2T %d",
+ value);
+ config->defaults.iscsi_options.
+ InitialR2T = value;
+ }
+ } else
+ log_error(
+ "error on line %d of %s, "
+ "invalid InitialR2T %s",
+ line_number, pathname, str);
+ } else if (strncasecmp(line, "ImmediateData=", 14) == 0) {
+ char *str = &line[14];
+ int value = parse_boolean(str);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_operational_config
+ *iscsi_options =
+ entry_iscsi_options
+ (current_entry);
+
+ if (iscsi_options) {
+ log_debug(5,
+ "config entry "
+ "%p "
+ "ImmediateData"
+ " %d",
+ current_entry,
+ value);
+ iscsi_options->
+ ImmediateData =
+ value;
+ } else {
+ log_error(
+ "Invalid "
+ "ImmediateData"
+ " entry on "
+ "line "
+ "%d of %s, "
+ "see man page"
+ " of iscsi."
+ "conf",
+ line_number,
+ pathname);
+ }
+ } else {
+ log_debug(5,
+ "config global "
+ "ImmediateData %d",
+ value);
+ config->defaults.iscsi_options.
+ ImmediateData = value;
+ }
+ } else
+ log_error(
+ "error on line %d of %s, "
+ "invalid ImmediateData %s",
+ line_number, pathname, str);
+ } else
+ if (strncasecmp
+ (line, "MaxRecvDataSegmentLength=", 25) == 0) {
+ char *num = &line[25];
+ int value = parse_number(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_operational_config
+ *iscsi_options =
+ entry_iscsi_options
+ (current_entry);
+
+ if (iscsi_options) {
+ log_debug(5,
+ "config entry "
+ "%p "
+ "MaxRecvDataSegmentLength "
+ "%d",
+ current_entry,
+ value);
+ iscsi_options->
+ MaxRecvDataSegmentLength
+ = value;
+ } else {
+ log_error(
+ "Invalid "
+ "MaxRecvDataSegmentLength"
+ " entry on line"
+ " %d of %s, "
+ "see man page"
+ " of iscsi."
+ "conf",
+ line_number,
+ pathname);
+ }
+ } else {
+ log_debug(5,
+ "config global "
+ "MaxRecvDataSegmentLength "
+ "%d",
+ value);
+ config->defaults.iscsi_options.
+ MaxRecvDataSegmentLength =
+ value;
+ }
+ } else
+ log_error(
+ "error on line %d of %s, invalid"
+ " MaxRecvDataSegmentLength %s",
+ line_number, pathname, num);
+ } else if (strncasecmp(line, "FirstBurstLength=", 17) ==
+ 0) {
+ char *num = &line[17];
+ int value = parse_number(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_operational_config
+ *iscsi_options =
+ entry_iscsi_options
+ (current_entry);
+
+ if (iscsi_options) {
+ log_debug(5,
+ "config entry "
+ "%p "
+ "FirstBurstLength"
+ " %d",
+ current_entry,
+ value);
+ iscsi_options->
+ FirstBurstLength =
+ value;
+ } else {
+ log_error(
+ "Invalid "
+ "FirstBurstLength"
+ " entry on line"
+ " %d of %s, "
+ "see man page "
+ "of iscsi."
+ "conf",
+ line_number,
+ pathname);
+ }
+ } else {
+ log_debug(5,
+ "config global "
+ "FirstBurstLength %d",
+ value);
+ config->defaults.iscsi_options.
+ FirstBurstLength = value;
+ }
+ } else
+ log_error(
+ "error on line %d of %s, "
+ "invalid FirstBurstLength %s",
+ line_number, pathname, num);
+ } else if (strncasecmp(line, "MaxBurstLength=", 15) ==
+ 0) {
+ char *num = &line[15];
+ int value = parse_number(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_operational_config
+ *iscsi_options =
+ entry_iscsi_options
+ (current_entry);
+
+ if (iscsi_options) {
+ log_debug(5,
+ "config entry "
+ "%p "
+ "MaxBurstLength"
+ " %d",
+ current_entry,
+ value);
+ iscsi_options->
+ MaxBurstLength =
+ value;
+ } else {
+ log_error(
+ "Invalid "
+ "MaxBurstLength"
+ " entry on line"
+ " %d of %s, "
+ "see man page "
+ "of iscsi."
+ "conf",
+ line_number,
+ pathname);
+ }
+ } else {
+ log_debug(5,
+ "config global "
+ "MaxBurstLength %d",
+ value);
+ config->defaults.iscsi_options.
+ MaxBurstLength = value;
+ }
+ } else
+ log_error(
+ "error on line %d of %s, invalid"
+ " MaxBurstLength %s",
+ line_number, pathname, num);
+ } else if (strncasecmp(line, "HeaderDigest=", 13) == 0) {
+ char *m = line + 13;
+ int digest = -1;
+
+ if ((strcasecmp(m, "never") == 0)
+ || (strcasecmp(m, "no") == 0)
+ || (strcasecmp(m, "none") == 0))
+ digest = CONFIG_DIGEST_NEVER;
+ else if ((strcasecmp(m, "always") == 0)
+ || (strcasecmp(m, "yes") == 0)
+ || (strcasecmp(m, "crc32c") == 0))
+ digest = CONFIG_DIGEST_ALWAYS;
+ else if ((strcasecmp(m, "prefer-on") == 0))
+ digest = CONFIG_DIGEST_PREFER_ON;
+ else if ((strcasecmp(m, "prefer-off") == 0))
+ digest = CONFIG_DIGEST_PREFER_OFF;
+ else {
+ digest = -1;
+ log_error(
+ "error on line %d of %s, invalid"
+ " HeaderDigest type %s\n",
+ line_number, pathname, m);
+ }
+
+ if (digest != -1) {
+ if (current_entry) {
+ struct iscsi_operational_config
+ *iscsi_options =
+ entry_iscsi_options
+ (current_entry);
+ if (iscsi_options) {
+ log_debug(5,
+ "config entry "
+ "%p "
+ "HeaderDigest "
+ "%d",
+ current_entry,
+ digest);
+ iscsi_options->
+ HeaderDigest =
+ digest;
+ } else {
+ log_error(
+ "error on line "
+ "%d of %s, "
+ "current entry "
+ "does not have "
+ "iSCSI settings,"
+ " invalid "
+ "HeaderDigest "
+ "%s",
+ line_number,
+ pathname, m);
+ }
+ } else {
+ /* default to use while
+ * processing the rest of the
+ * config file
+ */
+ log_debug(5,
+ "config global "
+ "HeaderDigest %d",
+ digest);
+ config->defaults.iscsi_options.
+ HeaderDigest = digest;
+ }
+ }
+ } else if (strncasecmp(line, "DataDigest=", 11) == 0) {
+ char *m = line + 11;
+ int digest = -1;
+
+ if ((strcasecmp(m, "never") == 0)
+ || (strcasecmp(m, "no") == 0)
+ || (strcasecmp(m, "none") == 0))
+ digest = CONFIG_DIGEST_NEVER;
+ else if ((strcasecmp(m, "always") == 0)
+ || (strcasecmp(m, "yes") == 0)
+ || (strcasecmp(m, "crc32c") == 0))
+ digest = CONFIG_DIGEST_ALWAYS;
+ else if ((strcasecmp(m, "prefer-on") == 0))
+ digest = CONFIG_DIGEST_PREFER_ON;
+ else if ((strcasecmp(m, "prefer-off") == 0))
+ digest = CONFIG_DIGEST_PREFER_OFF;
+ else {
+ digest = -1;
+ log_error(
+ "error on line %d of %s, invalid"
+ " DataDigest type %s\n",
+ line_number, pathname, m);
+ }
+
+ if (digest != -1) {
+ if (current_entry) {
+ struct iscsi_operational_config
+ *iscsi_options =
+ entry_iscsi_options
+ (current_entry);
+ if (iscsi_options) {
+ log_debug(5,
+ "config entry "
+ "%p "
+ "DataDigest "
+ "%d",
+ current_entry,
+ digest);
+ iscsi_options->
+ DataDigest = digest;
+ } else {
+ log_error(
+ "error on line "
+ "%d of %s, "
+ "current entry "
+ "does not have "
+ "iSCSI settings,"
+ " invalid "
+ "DataDigest "
+ "value %s",
+ line_number,
+ pathname, m);
+ }
+ } else {
+ /* default to use while
+ * processing the rest of the
+ * config file
+ */
+ log_debug(5,
+ "config global "
+ "DataDigest %d",
+ digest);
+ config->defaults.iscsi_options.
+ DataDigest = digest;
+ }
+ }
+ } else if (strncasecmp(line, "LoginTimeout=", 13) == 0) {
+ char *num = &line[13];
+ int value = parse_time(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_connection_timeout_config
+ *options =
+ entry_connection_timeout_options
+ (current_entry);
+
+ if (options) {
+ log_debug(5,
+ "config entry "
+ "%p "
+ "LoginTimeout "
+ "%d",
+ current_entry,
+ value);
+ options->login_timeout =
+ value;
+ } else {
+ log_error(
+ "error on line "
+ "%d of %s, "
+ "current entry "
+ "does not have "
+ "connection "
+ "timeout "
+ "settings, "
+ "invalid "
+ "LoginTimeout "
+ "%d, see man"
+ "page of iscsi"
+ ".conf",
+ line_number,
+ pathname, value);
+ }
+ } else {
+ log_debug(5,
+ "config global LoginTimeout %d",
+ value);
+ config->defaults.
+ connection_timeout_options.
+ login_timeout = value;
+ }
+ } else
+ log_error(
+ "error on line %d of %s, "
+ "invalid LoginTimeout %s",
+ line_number, pathname, num);
+ } else if (strncasecmp(line, "AuthTimeout=", 12) == 0) {
+ char *num = &line[12];
+ int value = parse_time(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_connection_timeout_config
+ *options =
+ entry_connection_timeout_options
+ (current_entry);
+
+ if (options) {
+ log_debug(5,
+ "config entry "
+ "%p "
+ "AuthTimeout "
+ "%d",
+ current_entry,
+ value);
+ options->auth_timeout =
+ value;
+ } else {
+ log_error(
+ "error on line "
+ "%d of %s, "
+ "current entry "
+ "does not have "
+ "connection "
+ "timeout "
+ "settings, "
+ "invalid "
+ "AuthTimeout %d"
+ ", see man "
+ "page of "
+ "iscsi.conf",
+ line_number,
+ pathname, value);
+ }
+ } else {
+ log_debug(5,
+ "config global "
+ "AuthTimeout %d",
+ value);
+ config->defaults.
+ connection_timeout_options.
+ auth_timeout = value;
+ }
+ } else
+ log_error(
+ "error on line %d of %s, "
+ "invalid AuthTimeout %s",
+ line_number, pathname, num);
+ } else if (strncasecmp(line, "ActiveTimeout=", 14) == 0) {
+ char *num = &line[14];
+ int value = parse_time(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_connection_timeout_config
+ *options =
+ entry_connection_timeout_options
+ (current_entry);
+
+ if (options) {
+ log_debug(5,
+ "config entry "
+ "%p "
+ "ActiveTimeout"
+ " %d",
+ current_entry,
+ value);
+ options->
+ active_timeout =
+ value;
+ } else {
+ log_error(
+ "error on line "
+ "%d of %s, "
+ "current entry "
+ "does not have "
+ "connection "
+ "timeout "
+ "settings, "
+ "invalid "
+ "ActiveTimeout "
+ "%d, see man "
+ "page of iscsi"
+ ".conf",
+ line_number,
+ pathname, value);
+ }
+ } else {
+ log_debug(5,
+ "config global "
+ "ActiveTimeout %d",
+ value);
+ config->defaults.
+ connection_timeout_options.
+ active_timeout = value;
+ }
+ } else
+ log_error(
+ "error on line %d of %s, "
+ "invalid ActiveTimeout %s",
+ line_number, pathname, num);
+ } else if (strncasecmp(line, "IdleTimeout=", 12) == 0) {
+ char *num = &line[12];
+ int value = parse_time(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_connection_timeout_config
+ *options =
+ entry_connection_timeout_options
+ (current_entry);
+
+ if (options) {
+ options->idle_timeout =
+ value;
+ log_debug(5,
+ "config entry "
+ "%p "
+ "IdleTimeout "
+ "%d",
+ current_entry,
+ value);
+ } else {
+ log_error(
+ "error on line "
+ "%d of %s, "
+ "current entry "
+ "does not have "
+ "connection "
+ "timeout "
+ "settings, "
+ "invalid "
+ "IdleTimeout %d"
+ ", see man page"
+ " of iscsi"
+ ".conf",
+ line_number,
+ pathname, value);
+ }
+ } else {
+ config->defaults.
+ connection_timeout_options.
+ idle_timeout = value;
+ log_debug(5,
+ "config global "
+ "IdleTimeout %d",
+ value);
+ }
+ } else
+ log_error(
+ "error on line %d of %s, "
+ "invalid IdleTimeout %s",
+ line_number, pathname, num);
+ } else if (strncasecmp(line, "PingTimeout=", 12) == 0) {
+ char *num = &line[12];
+ int value = parse_time(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_connection_timeout_config
+ *options =
+ entry_connection_timeout_options
+ (current_entry);
+
+ if (options) {
+ options->ping_timeout =
+ value;
+ log_debug(5,
+ "config entry "
+ "%p "
+ "PingTimeout "
+ "%d",
+ current_entry,
+ value);
+ } else {
+ log_error(
+ "error on line "
+ "%d of %s, "
+ "current entry "
+ "does not have "
+ "connection "
+ "timeout "
+ "settings, "
+ "invalid "
+ "PingTimeout %d"
+ ", see man "
+ "page of iscsi"
+ ".conf",
+ line_number,
+ pathname, value);
+ }
+ } else {
+ config->defaults.
+ connection_timeout_options.
+ ping_timeout = value;
+ log_debug(5,
+ "config global "
+ "PingTimeout %d",
+ value);
+ }
+ } else
+ log_error(
+ "error on line %d of %s, invalid"
+ " PingTimeout %s",
+ line_number, pathname, num);
+ } else if (strncasecmp(line, "AbortTimeout=", 13) == 0) {
+ char *num = &line[13];
+ int value = parse_time(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_error_timeout_config
+ *options =
+ entry_error_timeout_options
+ (current_entry);
+
+ if (options) {
+ options->abort_timeout =
+ value;
+ log_debug(5,
+ "config entry "
+ "%p "
+ "AbortTimeout "
+ "%d",
+ current_entry,
+ value);
+ } else {
+ log_error(
+ "error on line "
+ "%d of %s, "
+ "current entry "
+ "does not have "
+ "error timeout "
+ "settings, "
+ "invalid "
+ "AbortTimeout "
+ "%d, see man "
+ "page of iscsi"
+ ".conf",
+ line_number,
+ pathname, value);
+ }
+ } else {
+ config->defaults.
+ error_timeout_options.
+ abort_timeout = value;
+ log_debug(5,
+ "config global "
+ "AbortTimeout %d",
+ value);
+ }
+ } else
+ log_error(
+ "error on line %d of %s, "
+ "invalid AbortTimeout %s",
+ line_number, pathname, num);
+ } else if (strncasecmp(line, "ResetTimeout=", 13) == 0) {
+ char *num = &line[13];
+ int value = parse_time(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_error_timeout_config
+ *options =
+ entry_error_timeout_options
+ (current_entry);
+
+ if (options) {
+ options->reset_timeout =
+ value;
+ log_debug(5,
+ "config entry "
+ "%p "
+ "ResetTimeout "
+ "%d",
+ current_entry,
+ value);
+ } else {
+ log_error(
+ "error on line "
+ "%d of %s, "
+ "current entry "
+ "does not have "
+ "error timeout "
+ "settings, "
+ "invalid "
+ "ResetTimeout "
+ "%d, see man "
+ "page of iscsi"
+ ".conf",
+ line_number,
+ pathname, value);
+ }
+ } else {
+ config->defaults.
+ error_timeout_options.
+ abort_timeout = value;
+ log_debug(5,
+ "config global "
+ "ResetTimeout %d",
+ value);
+ }
+ } else
+ log_error(
+ "error on line %d of %s, "
+ "invalid ResetTimeout %s",
+ line_number, pathname, num);
+ } else if (strncasecmp(line, "ConnFailTimeout=", 16) ==
+ 0) {
+ char *num = &line[16];
+ int value = parse_time(num);
+
+ if (value >= 0) {
+ if (current_entry) {
+ struct iscsi_session_timeout_config
+ *options =
+ entry_session_timeout_options
+ (current_entry);
+
+ if (options) {
+ options->
+ replacement_timeout
+ = value;
+ log_debug(5,
+ "config entry "
+ "%p "
+ "ConnFailTimeout"
+ " %d",
+ current_entry,
+ value);
+ } else {
+ log_error(
+ "error on line "
+ "%d of %s, "
+ "current entry "
+ "does not have "
+ "session timeout"
+ " settings, "
+ "invalid "
+ "ConnFailTimeout"
+ " %d, see man"
+ "page of iscsi"
+ ".conf",
+ line_number,
+ pathname, value);
+ }
+ } else {
+ config->defaults.
+ session_timeout_options.
+ replacement_timeout = value;
+ log_debug(5,
+ "config global "
+ "ConnFailTimeout %d",
+ value);
+ }
+ } else {
+ log_error(
+ "error on line %d of %s, "
+ "invalid ConnFailTimeout %s",
+ line_number, pathname, num);
+ }
+ } else if (strncasecmp(line, "Target,Lun=", 11) == 0) {
+ /* do nothing, the LUN activator used to use
+ * these, but it's been replaced
+ */
+ } else if (*line && (*line != '#')) {
+ /* if it's not a comment, warn about it */
+ log_warning(
+ "error on line %d of %s, ignoring "
+ "unrecognized line %s",
+ line_number, pathname, line);
+ }
+ }
+ } while (line);
+
+ fclose(f);
+
+ if (!slp_multicast_seen && config->defaults.slp_multicast) {
+ /* FIXME: if SLP multicast is possible, but we didn't find a
+ * config file entry for it, assume SLPMulticast=all
+ */
+ }
+
+ log_debug(1, "updated config %p from %s", config, pathname);
+
+ return 1;
+}
+
+/* update the target config based on the config file */
+int
+update_target_config(struct iscsi_target_config *target,
+ struct iscsi_config *config,
+ struct iscsi_auth_config *auth_options)
+{
+ int ret = 1;
+
+ struct iscsi_config_entry *entry;
+
+ log_debug(5,
+ "setting defaults for target config %p to %s from config %p",
+ target, target->TargetName, config);
+
+ /* start with the global defaults */
+ target->enabled = config->defaults.enabled;
+
+ memcpy(&target->auth_options, &config->defaults.auth_options,
+ sizeof (target->auth_options));
+
+ /* the global authentication defaults can be overriden when a config is
+ * created, typically based on the authentication settings from a
+ * discovery process.
+ */
+ if (auth_options) {
+ /* these completely override the current settings, even if that
+ * means removing an existing username and password. This is
+ * needed to deal with some broken targets that can't do
+ * security phase properly.
+ * It must be possible to disable all authentication.
+ */
+ memcpy(&target->auth_options, auth_options,
+ sizeof (target->auth_options));
+ log_debug(5,
+ "overriding target config %p auth options %p in favor "
+ "of auth options %p\n",
+ target, &target->auth_options, auth_options);
+ }
+
+ /* apply the config file entries, which may override the defaults for
+ * particular targets or subnets, or for particular sections of
+ * the config file by resetting the globals between groups of
+ * TargetName or Subnet entries.
+ */
+ for (entry = config->head; entry; entry = entry->next) {
+ /* if the config entry is applicable to this target, apply it */
+ switch (entry->type) {
+ case CONFIG_TYPE_TARGETNAME:{
+ struct iscsi_targetname_config
+ *targetname_config =
+ entry->config.targetname;
+
+ if (strcmp
+ (target->TargetName,
+ targetname_config->TargetName) == 0) {
+ log_debug(5,
+ "applying config entry %p line"
+ " %d targetname config %p to "
+ "target config %p",
+ entry, entry->line_number,
+ targetname_config, target);
+
+ /* apply any target-wide settings */
+ target->enabled =
+ targetname_config->enabled;
+#ifdef PER_TARGETNAME_AUTH
+ /* FIXME: should we remove this, since
+ * it's more likely to cause problems
+ * than be a useful feature? When would
+ * a user need different credentials
+ * for the discovery and target logins?
+ */
+ log_debug(5,
+ "applying username %s password"
+ " %p to target config %p",
+ targetname_config->
+ auth_options.username,
+ targetname_config->
+ auth_options.password, target);
+ memcpy(&target->auth_options,
+ &targetname_config->auth_options,
+ sizeof (target->auth_options));
+#else
+ log_debug(5,
+ "target config %p currently "
+ "has username %s password %s",
+ target,
+ target->auth_options.username,
+ target->auth_options.password);
+#endif
+ } else {
+ log_debug(5,
+ "config entry %p line %d "
+ "targetname config %p does not"
+ " apply to target config %p",
+ entry, entry->line_number,
+ targetname_config, target);
+ }
+ break;
+ }
+ case CONFIG_TYPE_SUBNET:{
+ /* not relevant for a target config */
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* sanity check the target config */
+ if (target->auth_options.username_in[0]
+ || target->auth_options.password_length_in) {
+ /* if we're expecting incoming credentials, we must have
+ * outgoing credentials
+ */
+ if (target->auth_options.username[0] == '\0') {
+ log_error(
+ "target %s requires an outgoing username when "
+ "incoming credentials are expected\n",
+ target->TargetName);
+ ret = 0;
+ }
+ if (target->auth_options.password_length == 0) {
+ log_error(
+ "target %s requires an outgoing password when "
+ "incoming credentiuals are expected\n",
+ target->TargetName);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+/* update the session configs and portal configs, based on the config file */
+int
+update_session_configs(struct iscsi_target_config *target,
+ struct iscsi_config *config)
+{
+ struct iscsi_config_entry *entry;
+ struct iscsi_session_config *session_config;
+
+ /* start with the global defaults for each portal config
+ * of each new session
+ */
+ for (session_config = target->sessions; session_config;
+ session_config = session_config->next) {
+ struct iscsi_portal_config *portal_config;
+
+ portal_config = session_config->portal;
+
+ memcpy(&portal_config->connection_timeout_options,
+ &config->defaults.connection_timeout_options,
+ sizeof (portal_config->connection_timeout_options));
+ memcpy(&portal_config->session_timeout_options,
+ &config->defaults.session_timeout_options,
+ sizeof (portal_config->session_timeout_options));
+ memcpy(&portal_config->error_timeout_options,
+ &config->defaults.error_timeout_options,
+ sizeof (portal_config->error_timeout_options));
+ memcpy(&portal_config->tcp_options,
+ &config->defaults.tcp_options,
+ sizeof (portal_config->tcp_options));
+ memcpy(&portal_config->iscsi_options,
+ &config->defaults.iscsi_options,
+ sizeof (portal_config->iscsi_options));
+ }
+
+ /* apply the config file, which may override the defaults for
+ * particular targets or subnets, or for particular sections of
+ * the config file by resetting the globals between groups of
+ * TargetName or Subnet entries.
+ */
+ for (entry = config->head; entry; entry = entry->next) {
+ /* if the config entry is applicable to this session, apply it
+ */
+ switch (entry->type) {
+ case CONFIG_TYPE_TARGETNAME:{
+ struct iscsi_targetname_config
+ *targetname_config =
+ entry->config.targetname;
+
+ if (strcmp
+ (target->TargetName,
+ targetname_config->TargetName) == 0) {
+ log_debug(5,
+ "applying config entry %p line"
+ " %d targetname config %p to "
+ "target config %p sessions %p",
+ entry, entry->line_number,
+ targetname_config, target,
+ target->sessions);
+
+ for (session_config = target->sessions;
+ session_config;
+ session_config =
+ session_config->next) {
+ struct iscsi_portal_config
+ *portal_config;
+
+ /* apply these options to every
+ * portal for this session
+ */
+ portal_config =
+ session_config->portal;
+
+ log_debug(5, "applying "
+ "config entry %p "
+ "targetname config %p "
+ "to session #%d "
+ "config %p portal "
+ "config %p", entry,
+ targetname_config,
+ session_config->
+ path_number,
+ session_config,
+ portal_config);
+
+ memcpy(&portal_config->
+ tcp_options,
+ &targetname_config->
+ tcp_options,
+ sizeof(portal_config->
+ tcp_options));
+ memcpy(&portal_config->
+ connection_timeout_options,
+ &targetname_config->
+ connection_timeout_options,
+ sizeof(portal_config->
+ connection_timeout_options));
+ memcpy(&portal_config->
+ session_timeout_options,
+ &targetname_config->
+ session_timeout_options,
+ sizeof(portal_config->
+ session_timeout_options));
+ memcpy(&portal_config->
+ error_timeout_options,
+ &targetname_config->
+ error_timeout_options,
+ sizeof(portal_config->
+ error_timeout_options));
+ memcpy(&portal_config->
+ iscsi_options,
+ &targetname_config->
+ iscsi_options,
+ sizeof(portal_config->
+ iscsi_options));
+
+ }
+ } else {
+ log_debug(5,
+ "config entry %p line %d "
+ "targetname config %p does not"
+ " apply to target config %p",
+ entry, entry->line_number,
+ targetname_config, target);
+ }
+ break;
+ }
+ case CONFIG_TYPE_SUBNET:{
+ struct iscsi_subnet_config *subnet_config =
+ entry->config.subnet;
+
+ for (session_config = target->sessions;
+ session_config;
+ session_config = session_config->next) {
+ struct iscsi_portal_config
+ *portal_config;
+
+ /* apply these options to every portal
+ * for this session
+ */
+ portal_config =
+ session_config->portal;
+ /* FIXME: IPv6 */
+ if (portal_config->descriptor &&
+ portal_config->descriptor->ip_length ==
+ 4) {
+ uint32_t a1, a2;
+
+ a1 = portal_config->descriptor->
+ ip[0] << 24;
+ a1 |= portal_config->
+ descriptor->ip[1] << 16;
+ a1 |= portal_config->
+ descriptor->ip[2] << 8;
+ a1 |= portal_config->
+ descriptor->ip[3];
+ a1 &= subnet_config->
+ subnet_mask;
+
+ a2 = subnet_config->
+ ip_address[0] << 24;
+ a2 |= subnet_config->
+ ip_address[1] << 16;
+ a2 |= subnet_config->
+ ip_address[2] << 8;
+ a2 |= subnet_config->
+ ip_address[3];
+ a2 &= subnet_config->
+ subnet_mask;
+
+ if (a1 == a2) {
+ log_debug(5,
+ "applying config entry %p line %d subnet config %p to session config %p portal config %p",
+ entry,
+ entry->
+ line_number,
+ subnet_config,
+ session_config,
+ portal_config);
+
+ memcpy
+ (&portal_config->
+ connection_timeout_options,
+ &subnet_config->
+ connection_timeout_options,
+ sizeof
+ (portal_config->
+ connection_timeout_options));
+ memcpy
+ (&portal_config->
+ error_timeout_options,
+ &subnet_config->
+ error_timeout_options,
+ sizeof
+ (portal_config->
+ error_timeout_options));
+ memcpy
+ (&portal_config->
+ tcp_options,
+ &subnet_config->
+ tcp_options,
+ sizeof
+ (portal_config->
+ tcp_options));
+ } else {
+ log_debug(5,
+ "config entry %p line %d subnet config %p does not apply to target config %p",
+ entry,
+ entry->
+ line_number,
+ subnet_config,
+ target);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return 1;
+}
+
+/* create one or more session configs based on the target config. */
+void
+create_session_configs(struct iscsi_target_config *target,
+ struct iscsi_portal_descriptor *descriptors,
+ struct iscsi_config *config)
+{
+ struct iscsi_session_config *session = NULL;
+ struct iscsi_portal_descriptor *descriptor = NULL;
+ struct iscsi_portal_config *portal = NULL;
+ struct iscsi_session_config *prior = NULL;
+ int path_number = 1;
+
+ /* disabled targets have no sessions */
+ if (!target->enabled) {
+ log_debug(1, "target %p is disabled, creating no sessions to %s",
+ target, target->TargetName);
+ return;
+ }
+
+ log_debug(1, "creating session configs for target config %p to %s",
+ target, target->TargetName);
+
+
+ /* one session for each portal */
+ for (descriptor = descriptors; descriptor;
+ descriptor = descriptor->next) {
+ session = calloc(1, sizeof (*session));
+ if (session == NULL) {
+ log_error("couldn't allocate session "
+ "config for target %s",
+ target->TargetName);
+ break;
+ }
+
+ portal = calloc(1, sizeof (*portal));
+ if (portal == NULL) {
+ log_error("couldn't allocate portal "
+ "config for target %s",
+ target->TargetName);
+ break;
+ }
+
+ /* initialize session and portal */
+ session->next = NULL;
+ session->isid[0] = DRIVER_ISID_0;
+ session->isid[1] = DRIVER_ISID_1;
+ session->isid[2] = DRIVER_ISID_2;
+ session->isid[3] = (path_number >> 16) & 0xFF;
+ session->isid[4] = (path_number >> 8) & 0xFF;
+ session->isid[5] = (path_number) & 0xFF;
+ session->path_number = path_number++;
+ session->target = target;
+
+ portal->descriptor = descriptor;
+ /* only one portal */
+ portal->next = NULL;
+ session->portal = portal;
+
+ log_debug(1, "target config %p session #%d config %p portal %p "
+ "using descriptor %p", target, session->path_number, session, portal, descriptor);
+
+ /* add it to the list of new session configs */
+ if (prior) {
+ log_debug(7, "session %p portal %p follows session %p "
+ "for target %p", session, portal, prior,
+ target);
+ prior->next = session;
+ } else {
+ log_debug(7, "session %p portal %p is first session "
+ "for target %p", session, portal, target);
+ target->sessions = session;
+ }
+ prior = session;
+ /*FIXME what do I do with prior -krmurthy */
+ }
+
+ /* fill in the session and portal configs based on the config file info
+ */
+ update_session_configs(target, config);
+
+}
+
+struct iscsi_target_config *
+create_target_config(char *name, struct iscsi_portal_descriptor *descriptors,
+ struct iscsi_config *config,
+ struct iscsi_auth_config *auth_options)
+{
+ struct iscsi_target_config *target = calloc(1, sizeof (*target));
+
+ if (target) {
+ target->TargetName = name;
+ target->sessions = NULL;
+
+ log_debug(6, "allocated target config %p", target);
+
+ if (!update_target_config(target, config, auth_options)) {
+ free(target);
+ target = NULL;
+ }
+
+ create_session_configs(target, descriptors, config);
+ }
+
+ return target;
+}
diff --git a/usr/config.h b/usr/config.h
new file mode 100644
index 0000000..ba04ae3
--- /dev/null
+++ b/usr/config.h
@@ -0,0 +1,349 @@
+/*
+ * iSCSI Configuration
+ *
+ * Copyright (C) 2002 Cisco Systems, Inc.
+ * maintained by linux-iscsi-devel@lists.sourceforge.net
+ *
+ * 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 CONFIG_H
+#define CONFIG_H
+
+#include "types.h"
+#include "auth.h" /* for the username and password sizes */
+
+/* default iSCSI port number */
+#define ISCSI_DEFAULT_PORT 3260
+
+/* ISIDs now have a typed naming authority in them. We use an OUI */
+#define DRIVER_ISID_0 0x00
+#define DRIVER_ISID_1 0x02
+#define DRIVER_ISID_2 0x3D
+
+/* default window size */
+#define TCP_WINDOW_SIZE (256 * 1024)
+
+/* the following structures store the options set in the config file.
+ * a structure is defined for each logically-related group of options.
+ * if you are adding a new option, first check if it should belong
+ * to one of the existing groups. If it does, add it. If not, define
+ * a new structure.
+ */
+
+/* all authentication-related options should be added to this structure.
+ * this structure is per-session, and can be configured
+ * by TargetName but not Subnet.
+ */
+struct iscsi_auth_config {
+ unsigned int authmethod;
+ char username[AUTH_STR_MAX_LEN];
+ unsigned char password[AUTH_STR_MAX_LEN];
+ unsigned int password_length;
+ char username_in[AUTH_STR_MAX_LEN];
+ unsigned char password_in[AUTH_STR_MAX_LEN];
+ unsigned int password_length_in;
+};
+
+/* all per-connection timeouts go in this structure.
+ * this structure is per-portal, and can be configured
+ * both by TargetName and Subnet.
+ */
+struct iscsi_connection_timeout_config {
+ int login_timeout;
+ int auth_timeout;
+ int active_timeout;
+ int idle_timeout;
+ int ping_timeout;
+};
+
+/* all per-connection timeouts go in this structure.
+ * this structure is per-session, and can be configured
+ * by TargetName but not by Subnet.
+ */
+struct iscsi_session_timeout_config {
+ int replacement_timeout;
+};
+
+/* all error handling timeouts go in this structure.
+ * this structure is per-portal, and can be configured
+ * both by TargetName and Subnet.
+ */
+struct iscsi_error_timeout_config {
+ int abort_timeout;
+ int reset_timeout;
+};
+
+/* all TCP options go in this structure.
+ * this structure is per-portal, and can be configured
+ * both by TargetName and Subnet.
+ */
+struct iscsi_tcp_config {
+ int window_size;
+ int type_of_service; /* try to set IP TOS bits */
+};
+
+/* all iSCSI operational params go in this structure.
+ * this structure is per-portal, and can be configured
+ * both by TargetName and Subnet.
+ */
+struct iscsi_operational_config {
+ int protocol;
+ int InitialR2T;
+ int ImmediateData;
+ int MaxRecvDataSegmentLength;
+ int FirstBurstLength;
+ int MaxBurstLength;
+ int DefaultTime2Wait;
+ int DefaultTime2Retain;
+ int HeaderDigest;
+ int DataDigest;
+};
+
+#define CONFIG_DIGEST_NEVER 0
+#define CONFIG_DIGEST_ALWAYS 1
+#define CONFIG_DIGEST_PREFER_ON 2
+#define CONFIG_DIGEST_PREFER_OFF 3
+
+/* the following structures represent the contents of the config file */
+
+struct iscsi_targetname_config {
+ char *TargetName;
+ int enabled;
+ struct iscsi_auth_config auth_options;
+ struct iscsi_tcp_config tcp_options;
+ struct iscsi_connection_timeout_config connection_timeout_options;
+ struct iscsi_session_timeout_config session_timeout_options;
+ struct iscsi_error_timeout_config error_timeout_options;
+ struct iscsi_operational_config iscsi_options;
+};
+
+struct iscsi_subnet_config {
+ char *address;
+ int ip_length;
+ char ip_address[16];
+ int port;
+ uint32_t subnet_mask; /* IPv4 subnet mask */
+ struct iscsi_connection_timeout_config connection_timeout_options;
+ struct iscsi_error_timeout_config error_timeout_options;
+ struct iscsi_tcp_config tcp_options;
+};
+
+struct iscsi_sendtargets_config {
+ char *address;
+ int port;
+ int continuous;
+ int send_async_text;
+ struct iscsi_auth_config auth_options;
+ struct iscsi_connection_timeout_config connection_timeout_options;
+};
+
+struct iscsi_slp_config {
+ char *address; /* for unicast */
+ int port; /* for unicast */
+ char *scopes;
+ char *interfaces; /* for multicast, list of interfaces names,
+ * "all", or "none"
+ */
+ int poll_interval;
+ struct iscsi_auth_config auth_options;
+};
+
+struct iscsi_discovery_file_config {
+ char *filename;
+ int read_size;
+ char *address;
+ char *port;
+ int continuous;
+ struct iscsi_auth_config auth_options;
+};
+
+/* config defaults */
+struct iscsi_config_defaults {
+ /* discovery defaults */
+ int continuous_sendtargets; /* if non-zero, default to keeping
+ * iSCSI discovery sessions open
+ */
+ int send_async_text; /* if non-zero, target will send
+ * vendor specific async events.
+ */
+ int slp_multicast; /* if non-zero, default to attempting SLP
+ * multicast discovery of all targets this
+ * initiator can access
+ */
+ char *slp_scopes;
+ int slp_poll_interval;
+
+ /* global default options, used if no options are in the config,
+ * or if the user sets global defaults in the config file.
+ */
+ int enabled;
+ struct iscsi_auth_config auth_options;
+ struct iscsi_connection_timeout_config connection_timeout_options;
+ struct iscsi_error_timeout_config error_timeout_options;
+ struct iscsi_session_timeout_config session_timeout_options;
+ struct iscsi_tcp_config tcp_options;
+ struct iscsi_operational_config iscsi_options;
+};
+
+struct iscsi_config_entry {
+ struct iscsi_config_entry *prev;
+ struct iscsi_config_entry *next;
+ int type;
+ int line_number;
+ union {
+ struct iscsi_sendtargets_config *sendtargets;
+ struct iscsi_slp_config *slp;
+ struct iscsi_discovery_file_config *file;
+ struct iscsi_targetname_config *targetname;
+ struct iscsi_subnet_config *subnet;
+ } config;
+};
+
+#define CONFIG_TYPE_UNKNOWN 0
+#define CONFIG_TYPE_SENDTARGETS 1
+#define CONFIG_TYPE_SLP 2
+#define CONFIG_TYPE_DISCOVERY_FILE 3
+#define CONFIG_TYPE_TARGETNAME 4
+#define CONFIG_TYPE_SUBNET 5
+#define CONFIG_TYPE_ADDRESS 6
+
+/* collect all iscsi config file info together */
+struct iscsi_config {
+ struct iscsi_config_defaults defaults;
+ struct iscsi_config_entry *head;
+ struct iscsi_config_entry *tail;
+};
+
+/* the following structures represent the run-time configuration,
+ * computed based on the discovery info and the structures representing
+ * the config file contents.
+ */
+
+/* discovery produces portal descriptors.
+ * this arguably belongs in a different header file,
+ * but is here since everything references them
+ * via portal_configs
+ */
+struct iscsi_portal_descriptor {
+ struct iscsi_portal_descriptor *next;
+ char *address; /* text string */
+ char ip[16]; /* binary IP */
+ int ip_length;
+ int port;
+ int tag;
+};
+
+#define PORTAL_GROUP_TAG_UNKNOWN -1
+
+/* structures dynamically created as the main daemon collects discovery info
+ * and processes config info
+ */
+
+struct iscsi_portal_config {
+ struct iscsi_portal_config *next;
+ struct iscsi_portal_descriptor *descriptor; /* the target_config's
+ * portal descriptor
+ */
+ /* and the config options to use when the connection uses this portal */
+ struct iscsi_connection_timeout_config connection_timeout_options;
+ struct iscsi_session_timeout_config session_timeout_options;
+ struct iscsi_error_timeout_config error_timeout_options;
+ struct iscsi_tcp_config tcp_options;
+ struct iscsi_operational_config iscsi_options;
+};
+
+struct iscsi_portal_config_list {
+ struct iscsi_portal_config *head;
+ struct iscsi_portal_config *tail;
+};
+
+/* config code doesn't need to care about what's in the session process struct
+ */
+struct iscsi_session_process;
+
+struct iscsi_target_config;
+
+/* a normal iSCSI session */
+struct iscsi_session_config {
+ struct iscsi_session_config *next;
+ struct iscsi_session_process *process;
+ struct iscsi_target_config *target;
+ struct iscsi_portal_config *portal;
+ int iscsi_bus;
+ int target_id;
+ unsigned char isid[6];
+ int path_number;
+};
+
+struct iscsi_discovery_process;
+
+struct iscsi_target_config {
+ char *TargetName; /* typically shared with some other structure,
+ * which manages the lifetime
+ */
+
+ /* options that only make sense for the target as a whole */
+ int enabled;
+ struct iscsi_auth_config auth_options;
+
+ struct iscsi_session_config *sessions;
+
+};
+
+/* exported functions */
+extern char *get_iscsi_initiatorname(char *pathname);
+
+extern int update_iscsi_config(const char *pathname,
+ struct iscsi_config *config);
+
+extern int add_config_entry(struct iscsi_config *config,
+ struct iscsi_config_entry *entry);
+extern int remove_config_entry(struct iscsi_config *config,
+ struct iscsi_config_entry *entry);
+extern void free_config_entry(struct iscsi_config_entry *entry);
+
+extern struct iscsi_target_config *create_target_config(char *name,
+ struct
+ iscsi_portal_descriptor
+ *portals,
+ struct iscsi_config
+ *config,
+ struct iscsi_auth_config
+ *auth_options);
+
+extern void free_target_config(struct iscsi_target_config *config);
+
+extern void create_session_configs(struct iscsi_target_config *target,
+ struct iscsi_portal_descriptor *portals,
+ struct iscsi_config *config);
+
+extern void free_session_config(struct iscsi_session_config *config);
+
+extern void free_portal_descriptors(struct iscsi_portal_descriptor *portals);
+
+/* comparisons */
+extern int same_portal_descriptor(struct iscsi_portal_descriptor *p1,
+ struct iscsi_portal_descriptor *p2);
+extern int same_portal_descriptors(struct iscsi_portal_descriptor *portals1,
+ struct iscsi_portal_descriptor *portals2);
+extern int same_portal_config(struct iscsi_portal_config *p1,
+ struct iscsi_portal_config *p2);
+extern int same_portal_configs(struct iscsi_portal_config *portals1,
+ struct iscsi_portal_config *portals2);
+extern int same_session_config(struct iscsi_session_config *s1,
+ struct iscsi_session_config *s2);
+extern int same_target_config(struct iscsi_target_config *t1,
+ struct iscsi_target_config *t2);
+
+#endif /* CONFIG_H */
diff --git a/usr/discovery.c b/usr/discovery.c
new file mode 100644
index 0000000..875357d
--- /dev/null
+++ b/usr/discovery.c
@@ -0,0 +1,1785 @@
+/*
+ * iSCSI Discovery
+ *
+ * Copyright (C) 2002 Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * See the file COPYING included with this distribution for more details.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "strings.h"
+#include "iscsi_proto.h"
+#include "initiator.h"
+#include "iscsiadm.h"
+#include "config.h"
+#include "log.h"
+
+#ifdef SLP_ENABLE
+#include "iscsi-slp-discovery.h"
+#endif
+
+#define DISCOVERY_NEED_RECONNECT 0xdead0001
+
+static int rediscover = 0;
+static int record_begin;
+
+static void
+sighup_handler(int unused)
+{
+ rediscover = 1;
+}
+
+static int
+send_nop_reply(iscsi_session_t *session, iscsi_nopin_t *nop,
+ char *data, int timeout)
+{
+ iscsi_nopout_t out;
+
+ memset(&out, 0, sizeof (out));
+ out.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
+ out.flags = ISCSI_FLAG_CMD_FINAL;
+ memcpy(out.lun, nop->lun, sizeof (out.lun));
+ out.itt = nop->itt;
+ out.ttt = nop->ttt;
+ memcpy(out.dlength, nop->dlength, sizeof (out.dlength));
+ out.cmdsn = htonl(session->cmdsn); /* don't increment after
+ * immediate cmds
+ */
+ out.exp_statsn = htonl(session->exp_statsn);
+
+ log_debug(4, "sending nop reply for ttt %u, cmdsn %u, dlength %d",
+ ntohl(out.ttt), ntohl(out.cmdsn), ntoh24(out.dlength));
+
+ return iscsi_send_pdu(session, (iscsi_hdr_t *)&out,
+ ISCSI_DIGEST_NONE, data, ISCSI_DIGEST_NONE, timeout);
+}
+
+static int
+iscsi_make_text_pdu(iscsi_session_t *session, iscsi_hdr_t *hdr,
+ char *data, int max_data_length)
+{
+ iscsi_text_t *text_pdu = (iscsi_text_t *)hdr;
+
+ /* initialize the PDU header */
+ memset(text_pdu, 0, sizeof (*text_pdu));
+
+ text_pdu->opcode = ISCSI_OP_TEXT;
+ text_pdu->itt = htonl(session->itt);
+ text_pdu->ttt = ISCSI_RESERVED_TAG;
+ text_pdu->cmdsn = htonl(session->cmdsn++);
+ text_pdu->exp_statsn = htonl(session->exp_statsn);
+
+ return 1;
+}
+
+static int
+request_targets(iscsi_session_t *session)
+{
+ char data[64];
+ iscsi_text_t text;
+ iscsi_hdr_t *hdr = (iscsi_hdr_t *) &text;
+
+ memset(&text, 0, sizeof (text));
+ memset(data, 0, sizeof (data));
+
+ /* make a text PDU with SendTargets=All */
+ if (!iscsi_make_text_pdu(session, hdr, data, sizeof (data))) {
+ log_error("failed to make a SendTargets PDU");
+ return 0;
+ }
+
+ if (!iscsi_add_text
+ (session, hdr, data, sizeof (data), "SendTargets", "All")) {
+ log_error("failed to add SendTargets text key");
+ exit(1);
+ }
+
+ text.ttt = ISCSI_RESERVED_TAG;
+ text.flags = ISCSI_FLAG_CMD_FINAL;
+
+ if (++session->itt == ISCSI_RESERVED_TAG)
+ session->itt = 1;
+
+ if (!iscsi_send_pdu(session, hdr, ISCSI_DIGEST_NONE, data,
+ ISCSI_DIGEST_NONE, session->active_timeout)) {
+ log_error("failed to send SendTargets PDU");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+iterate_targets(iscsi_session_t *session, uint32_t ttt)
+{
+ char data[64];
+ iscsi_text_t text;
+ struct iscsi_hdr *pdu = (struct iscsi_hdr *) &text;
+
+ memset(&text, 0, sizeof (text));
+ memset(data, 0, sizeof (data));
+
+ /* make an empty text PDU */
+ if (!iscsi_make_text_pdu(session, pdu, data, sizeof (data))) {
+ log_error("failed to make an empty text PDU");
+ return 0;
+ }
+
+ text.ttt = ttt;
+ text.flags = ISCSI_FLAG_CMD_FINAL;
+
+ if (++session->itt == ISCSI_RESERVED_TAG)
+ session->itt = 1;
+
+ if (!iscsi_send_pdu(session, pdu, ISCSI_DIGEST_NONE, data,
+ ISCSI_DIGEST_NONE, session->active_timeout)) {
+ log_error("failed to send empty text PDU");
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+add_portal(struct string_buffer *info, char *address, char *port, char *tag)
+{
+ struct hostent *hostn = NULL;
+
+ /* resolve the address, in case it was a DNS name */
+ hostn = gethostbyname(address);
+ if (!hostn) {
+ log_error("cannot resolve %s", address);
+ return 0;
+ }
+
+ /* convert the resolved name to text */
+ if (hostn->h_length == 4) {
+ struct in_addr addr;
+
+ memcpy(&addr, hostn->h_addr, sizeof (addr));
+
+ if (tag && *tag) {
+ if (!append_sprintf(info, "TT=%s\n", tag)) {
+ log_error("couldn't add portal tag %s",
+ tag);
+ return 0;
+ }
+ }
+
+ if (port && *port) {
+ if (!append_sprintf(info, "TP=%s\n", port)) {
+ log_error("couldn't add port %s", port);
+ return 0;
+ }
+ }
+
+ if (strcmp(inet_ntoa(addr), address)) {
+ /* if the resolved name doesn't match the original,
+ * send an RA line as well as a TA line
+ */
+ return append_sprintf(info, "RA=%s\nTA=%s\n",
+ inet_ntoa(addr), address);
+ } else {
+ /* don't need the RA line */
+ return append_sprintf(info, "TA=%s\n", address);
+ }
+ } else {
+ /* FIXME: IPv6 */
+ log_error("can't handle network address %s", address);
+ return 0;
+ }
+}
+
+int
+add_target_record(struct string_buffer *info, char *name, char *end,
+ int lun_inventory_changed, char *default_address,
+ char *default_port, int fd)
+{
+ char *text = NULL;
+ char *nul = name;
+ size_t length;
+ size_t original = data_length(info);
+
+ /* address = IPv4
+ * address = [IPv6]
+ * address = DNSname
+ * address = IPv4:port
+ * address = [IPv6]:port
+ * address = DNSname:port
+ * address = IPv4,tag
+ * address = [IPv6],tag
+ * address = DNSname,tag
+ * address = IPv4:port,tag
+ * address = [IPv6]:port,tag
+ * address = DNSname:port,tag
+ */
+
+ log_debug(7, "adding target record %p, end %p", name, end);
+
+ /* find the end of the name */
+ while ((nul < end) && (*nul != '\0'))
+ nul++;
+
+ length = nul - name;
+ if (length > TARGET_NAME_MAXLEN) {
+ log_error("TargetName %s too long, ignoring", name);
+ return 0;
+ }
+
+ if (!record_begin) {
+ if (!append_sprintf
+ (info, lun_inventory_changed ? "DLC=%s\n" : "DTN=%s\n",
+ name)) {
+ log_error("couldn't report target %s", name);
+ truncate_buffer(info, original);
+ return 0;
+ }
+ record_begin = 1;
+ } else {
+ if (!append_sprintf
+ (info, lun_inventory_changed ? "LC=%s\n" : "TN=%s\n",
+ name)) {
+ log_error("couldn't report target %s", name);
+ truncate_buffer(info, original);
+ return 0;
+ }
+ }
+
+ text = name + length;
+
+ /* skip NULs after the name */
+ while ((text < end) && (*text == '\0'))
+ text++;
+
+ /* if no address is provided, use the default */
+ if (text >= end) {
+ if (default_address == NULL) {
+ log_error(
+ "no default address known for target %s", name);
+ truncate_buffer(info, original);
+ return 0;
+ } else
+ if (!add_portal(info, default_address, default_port, NULL))
+ {
+ log_error(
+ "failed to add default portal, ignoring "
+ "target %s", name);
+ truncate_buffer(info, original);
+ return 0;
+ } else if (!append_string(info, ";\n")) {
+ log_error(
+ "failed to terminate target record, "
+ "ignoring target %s", name);
+ truncate_buffer(info, original);
+ return 0;
+ }
+
+ /* finished adding the default */
+ return 1;
+ }
+
+ /* process TargetAddresses */
+ while (text < end) {
+ char *next = text + strlen(text) + 1;
+
+ log_debug(7, "text %p, next %p, end %p, %s", text, next, end,
+ text);
+
+ if (strncmp(text, "TargetAddress=", 14) == 0) {
+ char *port = NULL;
+ char *tag = NULL;
+ char *address = text + 14;
+
+ /* FIXME: handle IPv6 */
+ if (address[0] == '[') {
+ /* This is an IPv6 numeric address; skip it */
+ text = next;
+ continue;
+ }
+ if ((tag = strrchr(text, ','))) {
+ *tag = '\0';
+ tag++;
+ }
+ if ((port = strrchr(text, ':'))) {
+ *port = '\0';
+ port++;
+ }
+
+ if (!add_portal(info, address, port, tag)) {
+ log_error(
+ "failed to add default portal, "
+ "ignoring target %s", name);
+ truncate_buffer(info, original);
+ return 0;
+ }
+ } else {
+ log_error("unexpected SendTargets data: %s",
+ text);
+ }
+
+ text = next;
+ }
+
+ /* indicate the end of the target record */
+ if (!append_string(info, ";\n")) {
+ log_error(
+ "failed to terminate target record, ignoring target %s",
+ name);
+ truncate_buffer(info, original);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+process_sendtargets_response(struct string_buffer *sendtargets,
+ struct string_buffer *info, int final,
+ int lun_inventory_changed, char *default_address,
+ char *default_port, int fd)
+{
+ char *start = buffer_data(sendtargets);
+ char *text = start;
+ char *end = text + data_length(sendtargets);
+ char *nul = end - 1;
+ char *record = NULL;
+ int num_targets = 0;
+ size_t valid_info = 0;
+
+ if (start == end) {
+ /* no SendTargets data */
+ goto done;
+ }
+
+ /* scan backwards to find the last NUL in the data, to ensure we
+ * don't walk off the end. Since key=value pairs can span PDU
+ * boundaries, we're not guaranteed that the end of the data has a
+ * NUL.
+ */
+ while ((nul > start) && *nul)
+ nul--;
+
+ if (nul == start) {
+ /* couldn't find anything we can process now,
+ * it's one big partial string
+ */
+ goto done;
+ }
+
+ /* find the boundaries between target records (TargetName or final PDU)
+ */
+ for (;;) {
+ /* skip NULs */
+ while ((text < nul) && (*text == '\0'))
+ text++;
+
+ if (text == nul)
+ break;
+
+ log_debug(7,
+ "processing sendtargets record %p, text %p, line %s",
+ record, text, text);
+
+ /* look for the start of a new target record */
+ if (strncmp(text, "TargetName=", 11) == 0) {
+ if (record) {
+ /* send the last record, which we just found
+ * the end of. don't bother passing the
+ * "TargetName=" prefix.
+ */
+ if (!add_target_record
+ (info, record + 11, text,
+ lun_inventory_changed, default_address,
+ default_port, fd)) {
+ log_error(
+ "failed to add target record");
+ truncate_buffer(sendtargets, 0);
+ truncate_buffer(info, valid_info);
+ goto done;
+ }
+ num_targets++;
+ valid_info = data_length(info);
+ }
+ record = text;
+ }
+
+ /* everything up til the next NUL must be part of the
+ * current target record
+ */
+ while ((text < nul) && (*text != '\0'))
+ text++;
+ }
+
+ if (record) {
+ if (final) {
+ /* if this is the last PDU of the text sequence,
+ * it also ends a target record
+ */
+ log_debug(7,
+ "processing final sendtargets record %p, "
+ "line %s",
+ record, record);
+ if (add_target_record
+ (info, record + 11, text, lun_inventory_changed,
+ default_address, default_port, fd)) {
+ num_targets++;
+ record = NULL;
+ truncate_buffer(sendtargets, 0);
+ } else {
+ log_error("failed to add target record");
+ truncate_buffer(sendtargets, 0);
+ truncate_buffer(info, valid_info);
+ goto done;
+ }
+ } else {
+ /* remove the parts of the sendtargets buffer we've
+ * processed, and move the parts we haven't to the
+ * beginning of the buffer.
+ */
+ log_debug(7,
+ "processed %d bytes of sendtargets data, "
+ "%d remaining",
+ record - buffer_data(sendtargets),
+ buffer_data(sendtargets) +
+ data_length(sendtargets) - record);
+ remove_initial(sendtargets,
+ record - buffer_data(sendtargets));
+ }
+ }
+
+ done:
+ /* send all of the discovered targets to the parent daemon */
+ if (append_string(info, final ? "!\n" : ".\n")) {
+ write_buffer(info, fd);
+ truncate_buffer(info, 0);
+ if (final) {
+ record_begin = 0;
+ }
+ log_debug(4, "sent %d targets to parent daemon", num_targets);
+ return 1;
+ } else {
+ log_error("couldn't send %d targets to parent",
+ num_targets);
+ truncate_buffer(info, 0);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+add_async_record(struct string_buffer *info, char *record, int targetoffline,
+ int fd)
+{
+ int length = strlen(record);
+ size_t original = data_length(info);
+
+ log_debug(7, " adding async record for %s", record);
+
+ if (targetoffline) {
+ /* We have received targetoffline event */
+ if (length > TARGET_NAME_MAXLEN) {
+ log_error("Targetname %s too long, ignoring",
+ record);
+ return 0;
+ }
+ }
+ if (!append_sprintf
+ (info, targetoffline ? "ATF=%s\n" : "APF=%s\n", record)) {
+ log_error("couldn't report the record\n");
+ truncate_buffer(info, original);
+ return 0;
+ } else if (!append_string(info, ";\n")) {
+ log_error(
+ "failed to terminate target record, ignoring target %s",
+ record);
+ truncate_buffer(info, original);
+ }
+ return 1;
+}
+
+static void
+clear_timer(struct timeval *timer)
+{
+ memset(timer, 0, sizeof (*timer));
+}
+
+static int
+process_async_event_text(struct string_buffer *sendtargets,
+ struct string_buffer *info, struct timeval *timer,
+ int fd)
+{
+ char *text = buffer_data(sendtargets);
+ int targetoffline = 0;
+ int slen = (ntohs(*(short *) (text)));
+ text = text + 2 + slen;
+
+ if (strncmp(text, "X-com.cisco.targetOffline=", 26) == 0) {
+ targetoffline = 1;
+ if (!add_async_record(info, text + 26, targetoffline, fd)) {
+ log_error("failed to add async record");
+ return 0;
+ }
+ clear_timer(timer);
+ } else if (strncmp(text, "X-com.cisco.portalOffline=", 26) == 0) {
+ targetoffline = 0;
+ if (!add_async_record(info, text + 26, targetoffline, fd)) {
+ log_error("failed to add async record");
+ return 0;
+ }
+ clear_timer(timer);
+ } else {
+ log_debug(1, "sendtargets for the other events\n");
+ return 1;
+ }
+
+ if (append_string(info, "!\n")) {
+ write_buffer(info, fd);
+ truncate_buffer(info, 0);
+ log_debug(4, "sent async event record to parent daemon\n");
+ return 1;
+ } else {
+ log_error("couldn't send async event record \n");
+ return 0;
+ }
+}
+
+/* set timer to now + seconds */
+static void
+set_timer(struct timeval *timer, int seconds)
+{
+ if (timer) {
+ memset(timer, 0, sizeof (*timer));
+ gettimeofday(timer, NULL);
+
+ timer->tv_sec += seconds;
+ }
+}
+
+static int
+timer_expired(struct timeval *timer)
+{
+ struct timeval now;
+
+ /* no timer, can't have expired */
+ if ((timer == NULL) || ((timer->tv_sec == 0) && (timer->tv_usec == 0)))
+ return 0;
+
+ memset(&now, 0, sizeof (now));
+ gettimeofday(&now, NULL);
+
+ if (now.tv_sec > timer->tv_sec)
+ return 1;
+ if ((now.tv_sec == timer->tv_sec) && (now.tv_usec >= timer->tv_usec))
+ return 1;
+ return 0;
+}
+
+static int
+msecs_until(struct timeval *timer)
+{
+ struct timeval now;
+ int msecs;
+ long partial;
+
+ /* no timer, can't have expired, infinite time til it expires */
+ if ((timer == NULL) || ((timer->tv_sec == 0) && (timer->tv_usec == 0)))
+ return -1;
+
+ memset(&now, 0, sizeof (now));
+ gettimeofday(&now, NULL);
+
+ /* already expired? */
+ if (now.tv_sec > timer->tv_sec)
+ return 0;
+ if ((now.tv_sec == timer->tv_sec) && (now.tv_usec >= timer->tv_usec))
+ return 0;
+
+ /* not expired yet, do the math */
+ partial = timer->tv_usec - now.tv_usec;
+ if (partial < 0) {
+ partial += 1000 * 1000;
+ msecs = (partial + 500) / 1000;
+ msecs += (timer->tv_sec - now.tv_sec - 1) * 1000;
+ } else {
+ msecs = (partial + 500) / 1000;
+ msecs += (timer->tv_sec - now.tv_sec) * 1000;
+ }
+
+ return msecs;
+}
+
+static int
+soonest_msecs(struct timeval *t1, struct timeval *t2, struct timeval *t3)
+{
+ int m1 = msecs_until(t1);
+ int m2 = msecs_until(t2);
+ int m3 = msecs_until(t3);
+
+ /* infinity is -1, handle it specically */
+ if ((m1 == -1) && (m2 == -1))
+ return m3;
+
+ if ((m1 == -1) && (m3 == -1))
+ return m2;
+
+ if ((m2 == -1) && (m3 == -1))
+ return m1;
+
+ if (m1 == -1)
+ return (m2 < m3) ? m2 : m3;
+
+ if (m2 == -1)
+ return (m1 < m3) ? m1 : m3;
+
+ if (m3 == -1)
+ return (m1 < m2) ? m1 : m2;
+
+ if (m1 < m2)
+ return (m1 < m3) ? m1 : m3;
+ else
+ return (m2 < m3) ? m2 : m3;
+}
+
+static iscsi_session_t *
+init_new_session(struct iscsi_sendtargets_config *config,
+ struct iscsi_discovery_process *process)
+{
+ iscsi_session_t *session;
+
+ session = calloc(1, sizeof (*session));
+ if (session == NULL) {
+ log_error(
+ "discovery process to %s:%d failed to "
+ "allocate a session\n",
+ config->address, config->port);
+ goto done;
+ }
+
+ /* initialize the session */
+ session->socket_fd = -1;
+ session->type = ISCSI_SESSION_TYPE_DISCOVERY;
+ session->login_timeout =
+ config->connection_timeout_options.login_timeout;
+ session->auth_timeout = config->connection_timeout_options.auth_timeout;
+ session->active_timeout =
+ config->connection_timeout_options.active_timeout;
+ session->idle_timeout = config->connection_timeout_options.idle_timeout;
+ session->ping_timeout = config->connection_timeout_options.ping_timeout;
+ session->send_async_text =
+ config->continuous ? config->send_async_text : -1;
+ /* OUI and uniqifying number */
+ session->isid[0] = DRIVER_ISID_0;
+ session->isid[1] = DRIVER_ISID_1;
+ session->isid[2] = DRIVER_ISID_2;
+ session->isid[3] = (process->order >> 16) & 0xFF;
+ session->isid[4] = (process->order >> 8) & 0xFF;
+ session->isid[5] = (process->order >> 0) & 0xFF;
+ session->initiator_name = dconfig->initiator_name;
+ session->initiator_alias = dconfig->initiator_alias;
+ session->header_digest = ISCSI_DIGEST_NONE;
+ session->data_digest = ISCSI_DIGEST_NONE;
+ session->max_recv_data_segment_len =
+ DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH;
+ session->max_xmit_data_segment_len =
+ DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH;
+ session->portal_group_tag = PORTAL_GROUP_TAG_UNKNOWN;
+
+ log_debug(4,
+ "sendtargets discovery process %p to %s:%d using "
+ "isid 0x%02x%02x%02x%02x%02x%02x",
+ process, config->address, config->port, session->isid[0],
+ session->isid[1], session->isid[2], session->isid[3],
+ session->isid[4], session->isid[5]);
+ done:
+ return(session);
+}
+
+
+static int
+setup_authentication(iscsi_session_t *session,
+ struct iscsi_sendtargets_config *config)
+{
+ int rc;
+
+ rc = 1;
+
+ /* if we have any incoming credentials, we insist on authenticating
+ * the target or not logging in at all
+ */
+ if (config->auth_options.username_in[0]
+ || config->auth_options.password_length_in) {
+ session->bidirectional_auth = 1;
+
+ /* sanity check the config */
+ if ((config->auth_options.username[0] == '\0')
+ || (config->auth_options.password_length == 0)) {
+ log_error(
+ "discovery process to %s:%d has incoming "
+ "authentication credentials but has no outgoing "
+ "credentials configured\n",
+ config->address, config->port);
+ log_error(
+ "discovery process to %s:%d exiting, bad "
+ "configuration\n",
+ config->address, config->port);
+ rc = 0;
+ goto done;
+ }
+ } else {
+ /* no or 1-way authentication */
+ session->bidirectional_auth = 0;
+ }
+
+ /* copy in whatever credentials we have */
+ strncpy(session->username, config->auth_options.username,
+ sizeof (session->username));
+ session->username[sizeof (session->username) - 1] = '\0';
+ if ((session->password_length = config->auth_options.password_length))
+ memcpy(session->password, config->auth_options.password,
+ session->password_length);
+
+ strncpy(session->username_in, config->auth_options.username_in,
+ sizeof (session->username_in));
+ session->username_in[sizeof (session->username_in) - 1] = '\0';
+ if ((session->password_length_in =
+ config->auth_options.password_length_in))
+ memcpy(session->password_in, config->auth_options.password_in,
+ session->password_length_in);
+
+ if (session->password_length || session->password_length_in) {
+ /* setup the auth buffers */
+ session->auth_buffers[0].address = &session->auth_client_block;
+ session->auth_buffers[0].length =
+ sizeof (session->auth_client_block);
+ session->auth_buffers[1].address =
+ &session->auth_recv_string_block;
+ session->auth_buffers[1].length =
+ sizeof (session->auth_recv_string_block);
+
+ session->auth_buffers[2].address =
+ &session->auth_send_string_block;
+ session->auth_buffers[2].length =
+ sizeof (session->auth_send_string_block);
+
+ session->auth_buffers[3].address =
+ &session->auth_recv_binary_block;
+ session->auth_buffers[3].length =
+ sizeof (session->auth_recv_binary_block);
+
+ session->auth_buffers[4].address =
+ &session->auth_send_binary_block;
+ session->auth_buffers[4].length =
+ sizeof (session->auth_send_binary_block);
+
+ session->num_auth_buffers = 5;
+ } else {
+ session->num_auth_buffers = 0;
+ }
+ done:
+ return(rc);
+}
+
+static int
+process_recvd_pdu(struct iscsi_hdr *pdu,
+ struct iscsi_sendtargets_config *config,
+ iscsi_session_t *session,
+ struct string_buffer *sendtargets,
+ struct string_buffer *info,
+ int *lun_inventory_changed,
+ char *default_port,
+ struct iscsi_discovery_process *process,
+ int *active,
+ int *long_lived,
+ struct timeval *async_timer,
+ char *data)
+{
+ int rc=0;
+
+ switch (pdu->opcode) {
+ case ISCSI_OP_TEXT_RSP:{
+ iscsi_text_rsp_t *text_response =
+ (iscsi_text_rsp_t *) pdu;
+ int dlength = ntoh24(pdu->dlength);
+ int final = (text_response->flags & ISCSI_FLAG_CMD_FINAL)||
+ (text_response-> ttt == ISCSI_RESERVED_TAG);
+
+ log_debug(4, "discovery session to %s:%d received text"
+ " response, %d data bytes, ttt 0x%x, "
+ "final 0x%x",
+ config->address,
+ config->port,
+ dlength,
+ ntohl(text_response->ttt),
+ text_response->flags & ISCSI_FLAG_CMD_FINAL);
+
+ /* mark how much more data in the sendtargets
+ * buffer is now valid
+ */
+ enlarge_data (sendtargets, dlength);
+
+ /* process as much as we
+ * can right now
+ */
+ process_sendtargets_response (sendtargets,
+ info, final,
+ *lun_inventory_changed,
+ config->address,
+ default_port,
+ process->pipe_fd);
+
+ if (final) {
+ /* SendTargets exchange is now complete
+ */
+ *active = 0;
+ *lun_inventory_changed = 1;
+ /* from now on, after any reconnect,
+ * assume LUNs may have changed
+ */
+ } else {
+ /* ask for more targets */
+ if (!iterate_targets(session,
+ text_response->ttt)) {
+ rc = DISCOVERY_NEED_RECONNECT;
+ goto done;
+ }
+ }
+ break;
+ }
+ case ISCSI_OP_ASYNC_EVENT:{
+ iscsi_async_t *async_hdr =
+ (iscsi_async_t *) pdu;
+ int dlength = ntoh24(pdu->dlength);
+ short senselen;
+ char logbuf[128];
+ int i;
+
+ /*
+ * If we receive an async message stating
+ * the target wants to close the connection,
+ * then don't try to reconnect anymore.
+ * This is reasonable, so we don't log
+ * anything here.
+ */
+ if ((async_hdr->async_event ==
+ ISCSI_ASYNC_MSG_REQUEST_LOGOUT)||
+ (async_hdr->async_event ==
+ ISCSI_ASYNC_MSG_DROPPING_CONNECTION)||
+ (async_hdr->async_event ==
+ ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS)) {
+ *long_lived=0;
+ break;
+ }
+
+ /*
+ * Log info about the async message.
+ */
+ log_warning(
+ "Received Async Msg from target, Event = %d, "
+ "Code = %d, Data Len = %d\n",
+ async_hdr->async_event,
+ async_hdr->async_vcode, dlength);
+
+ /*
+ * If there was data, print out the first 8 bytes
+ */
+ if (dlength > 0) {
+ memset(logbuf, 0, sizeof(logbuf));
+ for (i=0; i<8 && i<dlength; ++i) {
+ sprintf(logbuf+i*5, "0x%02x ",
+ data[i]);
+ }
+ log_warning(" Data[0]-[%d]: %s\n",
+ i<dlength ? dlength-1 : i-1,
+ logbuf);
+ }
+
+
+ if (dlength > (sizeof (short))) {
+ senselen = (ntohs(*(short *) (data)));
+
+ log_debug(1, " senselen = %d\n", senselen);
+ if (dlength > senselen + 2) {
+ log_debug(1,
+ " recvd async event : %s\n",
+ data + 2 + senselen);
+ }
+ }
+ *long_lived = 1;
+
+ /*
+ * Arrange for a rediscovery to occur in the near
+ * future. We use a timer so that we merge
+ * multiple events that occur in rapid succession,
+ * and only rediscover once for each burst of
+ * Async events.
+ */
+ set_timer(async_timer, 1);
+ if (*active) {
+ log_debug(4,
+ "discovery process %p received Async "
+ "event while active", process);
+ } else {
+ log_debug(4,
+ "discovery process %p received Async "
+ "event while idle", process);
+ }
+ process_async_event_text(sendtargets, info,
+ async_timer,
+ process->pipe_fd);
+ break;
+ }
+ case ISCSI_OP_NOOP_IN:{
+ iscsi_nopin_t *nop =
+ (iscsi_nopin_t *) pdu;
+
+ /*
+ * The iSCSI spec doesn't allow Nops on
+ * discovery sessions, but some targets
+ * use them anyway. If we receive one, we
+ * can safely assume that the target
+ * supports long-lived discovery sessions
+ * (otherwise it wouldn't be sending nops
+ * to verify the connection is still
+ * working).
+ */
+ *long_lived = 1;
+ log_debug(4,"discovery session to %s:%d received"
+ " Nop-in with itt %u, ttt %u, dlength %u",
+ config->address, config->port,
+ ntohl(nop->itt), ntohl(nop->ttt),
+ ntoh24(nop->dlength));
+
+ if (nop->ttt != ISCSI_RESERVED_TAG) {
+ /* reply to the Nop-in */
+ if (!send_nop_reply(session, nop, data,
+ session->active_timeout)) {
+ log_error(
+ "discovery session to %s:%d "
+ "failed to send Nop reply, "
+ "ttt %u, reconnecting",
+ config->address,
+ config->port,
+ ntohl(nop->ttt));
+ rc = DISCOVERY_NEED_RECONNECT;
+ goto done;
+ }
+ }
+ break;
+ }
+ case ISCSI_OP_REJECT:{
+ iscsi_reject_t *reject =
+ (iscsi_reject_t *) pdu;
+ int dlength = ntoh24(pdu->dlength);
+
+ log_error("reject, dlength=%d, "
+ "data[0]=0x%x\n",
+ dlength, data[0]);
+ log_error(
+ "Received a reject from the target "
+ "with reason code = 0x%x\n",
+ reject->reason);
+ /*
+ * Just attempt to reconnect if we receive a reject
+ */
+ rc = DISCOVERY_NEED_RECONNECT;
+ goto done;
+ break;
+ }
+ default:{
+ log_warning(
+ "discovery session to %s:%d received "
+ "unexpected opcode 0x%x",
+ config->address, config->port, pdu->opcode);
+ rc = DISCOVERY_NEED_RECONNECT;
+ goto done;
+ }
+ }
+ done:
+ return(rc);
+}
+
+/*
+ * Make a best effort to logout the session, then disconnect the
+ * socket.
+ */
+static void
+iscsi_logout_and_disconnect(iscsi_session_t * session)
+{
+ iscsi_logout_t logout_req;
+ iscsi_logout_rsp_t logout_resp;
+ int rc;
+
+ /*
+ * Build logout request header
+ */
+ memset(&logout_req, 0, sizeof (logout_req));
+ logout_req.opcode = ISCSI_OP_LOGOUT | ISCSI_OP_IMMEDIATE;
+ logout_req.flags = ISCSI_FLAG_CMD_FINAL |
+ (ISCSI_LOGOUT_REASON_CLOSE_SESSION &
+ ISCSI_FLAG_LOGOUT_REASON_MASK);
+ logout_req.itt = htonl(session->itt);
+ if (++session->itt == ISCSI_RESERVED_TAG)
+ session->itt = 1;
+ logout_req.cmdsn = htonl(session->cmdsn);
+ logout_req.exp_statsn = htonl(++session->exp_statsn);
+
+ /*
+ * Send the logout request
+ */
+ rc = iscsi_send_pdu(session, (struct iscsi_hdr *)&logout_req,
+ ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 3);
+ if (!rc) {
+ log_error(
+ "iscsid: iscsi_logout - failed to send logout PDU.\n");
+ goto done;
+ }
+
+ /*
+ * Read the logout response
+ */
+ memset(&logout_resp, 0, sizeof(logout_resp));
+ rc = iscsi_recv_pdu(session, (struct iscsi_hdr *)&logout_resp,
+ ISCSI_DIGEST_NONE, NULL, 0, ISCSI_DIGEST_NONE, 1);
+ if (!rc) {
+ log_error(
+ "iscsid: logout - failed to receive logout resp\n");
+ goto done;
+ }
+ if (logout_resp.response != ISCSI_LOGOUT_SUCCESS) {
+ log_error("iscsid: logout failed - response = 0x%x\n",
+ logout_resp.response);
+ }
+
+done:
+ /*
+ * Close the socket.
+ */
+ iscsi_disconnect(session);
+}
+
+static void
+sendtargets_discovery_process(struct iscsi_discovery_process *process)
+{
+ struct iscsi_sendtargets_config *config =
+ process->entry->config.sendtargets;
+ struct sigaction action;
+ iscsi_session_t *session;
+ struct hostent *hostn = NULL;
+ struct pollfd pfd;
+ struct iscsi_hdr pdu_buffer;
+ struct iscsi_hdr *pdu = &pdu_buffer;
+ char *data = NULL;
+ char *end_of_data;
+ int long_lived = (config->continuous > 0) ? 1 : 0;
+ int lun_inventory_changed = 0;
+ int active = 0;
+ struct timeval connection_timer, async_timer;
+ int timeout;
+ int rc;
+ struct string_buffer sendtargets;
+ struct string_buffer info;
+ uint8_t status_class = 0, status_detail = 0;
+ unsigned int login_failures = 0;
+ int login_delay = 0;
+ char ip_address[16];
+ char default_port[12];
+ int ip_length = 0;
+ int port = config->port;
+
+ /* initial setup */
+ process->pid = getpid();
+ log_debug(1,
+ "sendtargets discovery process %p starting, address %s:%d, "
+ "continuous %d",
+ process, config->address, config->port, config->continuous);
+
+ memset(&pdu_buffer, 0, sizeof (pdu_buffer));
+ clear_timer(&connection_timer);
+ clear_timer(&async_timer);
+
+ /* set SIGTERM, SIGINT to kill the process */
+ memset(&action, 0, sizeof (struct sigaction));
+ action.sa_sigaction = NULL;
+ action.sa_flags = 0;
+ action.sa_handler = SIG_DFL;
+ sigaction(SIGTERM, &action, NULL);
+ sigaction(SIGINT, &action, NULL);
+
+ /* set SIGPIPE to be ignored, so that we get EPIPE */
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+
+ /* set SIGHUP to request a rediscovery */
+ action.sa_handler = sighup_handler;
+ sigaction(SIGHUP, &action, NULL);
+
+ /* allocate data buffers for SendTargets data and discovery pipe info */
+ init_string_buffer(&sendtargets, 32 * 1024);
+ init_string_buffer(&info, 8 * 1024);
+
+ /* allocate a new session, and initialize default values */
+ session = init_new_session(config, process);
+ if (session == NULL) {
+ exit(1);
+ }
+
+ /* resolve the DiscoveryAddress to an IP address */
+ while (!hostn) {
+ hostn = gethostbyname(config->address);
+ if (hostn) {
+ /* save the resolved address */
+ port = config->port;
+ ip_length = hostn->h_length;
+ memcpy(&ip_address, hostn->h_addr,
+ MIN(sizeof (ip_address), hostn->h_length));
+ /* FIXME: IPv6 */
+ log_debug(4, "resolved %s to %u.%u.%u.%u\n",
+ config->address, ip_address[0], ip_address[1],
+ ip_address[2], ip_address[3]);
+ } else {
+ log_error("cannot resolve host name %s",
+ config->address);
+ sleep(1);
+ }
+ }
+
+ sprintf(default_port, "%d", config->port);
+
+ log_debug(4,
+ "discovery timeouts: login %d, auth %d, active %d, "
+ "idle %d, ping %d",
+ session->login_timeout, session->auth_timeout,
+ session->active_timeout, session->idle_timeout,
+ session->ping_timeout);
+
+ /* setup authentication variables for the session*/
+ rc = setup_authentication(session, config);
+ if (rc == 0)
+ exit(0);
+
+
+
+ set_address:
+ /*
+ * copy the saved address to the session,
+ * undoing any temporary redirect
+ */
+ session->port = port;
+ session->ip_length = ip_length;
+ memcpy(session->ip_address, ip_address,
+ MIN(sizeof (session->ip_address), ip_length));
+
+ reconnect:
+
+ iscsi_disconnect(session);
+
+ session->cmdsn = 1;
+ session->itt = 1;
+ session->portal_group_tag = PORTAL_GROUP_TAG_UNKNOWN;
+
+ /*
+ * if we're violating the protocol anyway, there's no reason
+ * to be picky about sending keys.
+ */
+ session->vendor_specific_keys = long_lived;
+
+ /* slowly back off the frequency of login attempts */
+ if (login_failures == 0)
+ login_delay = 0;
+ else if (login_failures < 10)
+ login_delay = 1; /* 10 seconds at 1 sec each */
+ else if (login_failures < 20)
+ login_delay = 2; /* 20 seconds at 2 sec each */
+ else if (login_failures < 26)
+ login_delay = 5; /* 30 seconds at 5 sec each */
+ else if (login_failures < 34)
+ login_delay = 15; /* 60 seconds at 15 sec each */
+ else
+ login_delay = 60; /* after 2 minutes, try once a minute */
+
+ if (login_delay) {
+ log_debug(4,
+ "discovery session to %s:%d sleeping for %d "
+ "seconds before next login attempt",
+ config->address, config->port, login_delay);
+ sleep(login_delay);
+ }
+
+ if (!iscsi_connect(session)) {
+ /* FIXME: IPv6 */
+ log_error("Connection to Discovery Address %u.%u.%u.%u "
+ "failed", session->ip_address[0], session->ip_address[1],
+ session->ip_address[2], session->ip_address[3]);
+
+ login_failures++;
+ /* If a temporary redirect sent us to something unreachable,
+ * we want to go back to the original IP address, so make sure
+ * we reset the session's IP.
+ */
+ goto set_address;
+ }
+ log_warning("Connected to Discovery Address %u.%u.%u.%u",
+ session->ip_address[0], session->ip_address[1],
+ session->ip_address[2], session->ip_address[3]);
+
+ log_debug(4, "discovery session to %s:%d starting iSCSI login on fd %d",
+ config->address, config->port, session->socket_fd);
+ status_class = 0;
+ status_detail = 0;
+ switch (iscsi_login
+ (session, buffer_data(&sendtargets),
+ unused_length(&sendtargets), &status_class, &status_detail)) {
+ case LOGIN_OK:
+ break;
+
+ case LOGIN_IO_ERROR:
+ case LOGIN_WRONG_PORTAL_GROUP:
+ case LOGIN_REDIRECTION_FAILED:
+ /* try again */
+ /* FIXME: IPv6 */
+ log_warning("retrying discovery login to %u.%u.%u.%u",
+ session->ip_address[0], session->ip_address[1],
+ session->ip_address[2], session->ip_address[3]);
+ iscsi_disconnect(session);
+ login_failures++;
+ goto set_address;
+
+ default:
+ case LOGIN_FAILED:
+ case LOGIN_NEGOTIATION_FAILED:
+ case LOGIN_AUTHENTICATION_FAILED:
+ case LOGIN_VERSION_MISMATCH:
+ case LOGIN_INVALID_PDU:
+ /* FIXME: IPv6 */
+ log_error(
+ "discovery login to %u.%u.%u.%u failed, giving up",
+ session->ip_address[0], session->ip_address[1],
+ session->ip_address[2], session->ip_address[3]);
+ iscsi_disconnect(session);
+ exit(0);
+ }
+
+ /* check the login status */
+ switch (status_class) {
+ case ISCSI_STATUS_CLS_SUCCESS:
+ /* FIXME: IPv6 */
+ log_debug(4, "discovery login success to %u.%u.%u.%u",
+ session->ip_address[0], session->ip_address[1],
+ session->ip_address[2], session->ip_address[3]);
+ login_failures = 0;
+ break;
+ case ISCSI_STATUS_CLS_REDIRECT:
+ switch (status_detail) {
+ /* the session IP address was changed by the login
+ * library, so just try again with this portal
+ * config but the new address.
+ */
+ case ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP:
+ /* FIXME: IPv6 */
+ log_warning(
+ "discovery login temporarily redirected to "
+ "%u.%u.%u.%u port %d\n",
+ session->ip_address[0], session->ip_address[1],
+ session->ip_address[2], session->ip_address[3],
+ session->port);
+ goto reconnect;
+ case ISCSI_LOGIN_STATUS_TGT_MOVED_PERM:
+ /* FIXME: IPv6 */
+ log_warning(
+ "discovery login permanently redirected to "
+ "%u.%u.%u.%u port %d\n",
+ session->ip_address[0], session->ip_address[1],
+ session->ip_address[2], session->ip_address[3],
+ session->port);
+ /* make the new address permanent */
+ ip_length = session->ip_length;
+ memcpy(ip_address, session->ip_address,
+ MIN(sizeof (ip_address), session->ip_length));
+ port = session->port;
+
+ goto reconnect;
+ default:
+ log_error(
+ "discovery login rejected: redirection type "
+ "0x%x not supported\n",
+ status_detail);
+ goto set_address;
+ }
+ break;
+ case ISCSI_STATUS_CLS_INITIATOR_ERR:
+ /* FIXME: IPv6 */
+ log_error(
+ "discovery login to %u.%u.%u.%u rejected: "
+ "initiator error (%02x/%02x), non-retryable, giving up",
+ session->ip_address[0], session->ip_address[1],
+ session->ip_address[2], session->ip_address[3],
+ status_class, status_detail);
+ iscsi_disconnect(session);
+ exit(0);
+ case ISCSI_STATUS_CLS_TARGET_ERR:
+ /* FIXME: IPv6 */
+ log_error(
+ "discovery login to %u.%u.%u.%u rejected: "
+ "target error (%02x/%02x)",
+ session->ip_address[0], session->ip_address[1],
+ session->ip_address[2], session->ip_address[3],
+ status_class, status_detail);
+ iscsi_disconnect(session);
+ login_failures++;
+ goto reconnect;
+ default:
+ /* FIXME: IPv6 */
+ log_error(
+ "discovery login to %u.%u.%u.%u failed, response "
+ "with unknown status class 0x%x, detail 0x%x",
+ session->ip_address[0], session->ip_address[1],
+ session->ip_address[2], session->ip_address[3],
+ status_class, status_detail);
+ iscsi_disconnect(session);
+ login_failures++;
+ goto reconnect;
+ }
+
+ rediscover:
+ /* reinitialize */
+ truncate_buffer(&sendtargets, 0);
+ truncate_buffer(&info, 0);
+
+ /* we're going to do a discovery regardless */
+ clear_timer(&async_timer);
+
+ /* ask for targets */
+ if (!request_targets(session)) {
+ goto reconnect;
+ }
+ active = 1;
+
+ /* set timeouts */
+ if (long_lived) {
+ clear_timer(&connection_timer);
+ } else {
+ set_timer(&connection_timer,
+ session->active_timeout + session->ping_timeout);
+ }
+
+ /* prepare to poll */
+ memset(&pfd, 0, sizeof (pfd));
+ pfd.fd = session->socket_fd;
+ pfd.events = POLLIN | POLLPRI;
+
+ /* check timers before blocking */
+ if (timer_expired(&connection_timer)) {
+ if (long_lived || !lun_inventory_changed) {
+ /* long-lived, or never finished the first
+ * exchange (might be long-lived)
+ */
+ clear_timer(&connection_timer);
+ log_debug(1,
+ "discovery session to %s:%d "
+ "reconnecting after connection timeout",
+ config->address, config->port);
+ goto reconnect;
+ } else {
+ log_warning(
+ "discovery session to %s:%d session "
+ "logout, connection timer expired",
+ config->address, config->port);
+ iscsi_logout_and_disconnect(session);
+ exit(0);
+ }
+ }
+
+ if (active) {
+ /* ignore the async timer, we're in the middle
+ * of a discovery
+ */
+ timeout = msecs_until(&connection_timer);
+ } else {
+ /* to avoid doing LUN probing repeatedly, try to merge
+ * multiple Async PDUs into one rediscovery by
+ * deferring discovery until a timeout expires.
+ */
+ if (timer_expired(&async_timer)) {
+ log_debug(4,
+ "discovery session to %s:%d async "
+ "timer expired, rediscovering",
+ config->address, config->port);
+ clear_timer(&async_timer);
+ goto rediscover;
+ } else
+ timeout =
+ soonest_msecs(NULL,
+ &connection_timer,
+ &async_timer);
+ }
+
+ /* block until we receive a PDU, a TCP FIN, a TCP RST,
+ * or a timeout
+ */
+ log_debug(4,
+ "discovery process %s:%d polling fd %d, "
+ "timeout in %f seconds",
+ config->address, config->port, pfd.fd,
+ timeout / 1000.0);
+
+ pfd.revents = 0;
+ rc = poll(&pfd, 1, timeout);
+
+ log_debug(7,
+ "discovery process to %s:%d returned from poll, rc %d",
+ config->address, config->port, rc);
+
+ if (rc > 0) {
+ if (pfd.revents & (POLLIN | POLLPRI)) {
+ /* put any PDU data into the
+ * sendtargets buffer for now
+ */
+ data = buffer_data(&sendtargets) +
+ data_length(&sendtargets);
+ end_of_data =
+ data + unused_length(&sendtargets);
+ timeout = msecs_until(&connection_timer);
+
+ if (iscsi_recv_pdu
+ (session, pdu, ISCSI_DIGEST_NONE, data,
+ end_of_data - data, ISCSI_DIGEST_NONE,
+ timeout)) {
+ /*
+ * process iSCSI PDU received
+ */
+ rc = process_recvd_pdu(
+ pdu,
+ config,
+ session,
+ &sendtargets,
+ &info,
+ &lun_inventory_changed,
+ default_port,
+ process,
+ &active,
+ &long_lived,
+ &async_timer,
+ data);
+ if (rc == DISCOVERY_NEED_RECONNECT)
+ goto reconnect;
+
+ /* reset timers after receiving a PDU */
+ if (long_lived) {
+ clear_timer(&connection_timer);
+ } else {
+ if (active)
+ set_timer
+ (&connection_timer,
+ session->
+ active_timeout);
+ else
+ /*
+ * 3 minutes to try
+ * to go long-lived
+ */
+ set_timer(&connection_timer, 3 * 60);
+ }
+ } else {
+ if (long_lived) {
+ log_debug(1,
+ "discovery session to "
+ "%s:%d failed to recv a "
+ "PDU response, "
+ "reconnecting",
+ config->address,
+ config->port);
+ goto reconnect;
+ } else {
+ log_debug(1,
+ "discovery session to "
+ "%s:%d failed to recv a "
+ "PDU response, "
+ "terminating",
+ config->address,
+ config->port);
+ iscsi_disconnect(session);
+ exit(0);
+ }
+ }
+ }
+ if (pfd.revents & POLLHUP) {
+ if (long_lived) {
+ log_warning(
+ "discovery session to %s:%d "
+ "reconnecting after hangup",
+ config->address, config->port);
+ goto reconnect;
+ } else {
+ log_warning(
+ "discovery session to %s:%d "
+ "terminating after hangup",
+ config->address, config->port);
+ iscsi_disconnect(session);
+ exit(0);
+ }
+ }
+
+ if (pfd.revents & POLLNVAL) {
+ log_warning("discovery POLLNVAL");
+ sleep(1);
+ goto reconnect;
+ }
+
+ if (pfd.revents & POLLERR) {
+ log_warning("discovery POLLERR");
+ sleep(1);
+ goto reconnect;
+ }
+ } else if (rc < 0) {
+ if (errno == EINTR) {
+ /* if we got SIGHUP, reconnect and rediscover */
+ if (rediscover) {
+ rediscover = 0;
+ log_debug(1, "rediscovery requested");
+ goto reconnect;
+ }
+ } else {
+ log_error("poll error");
+ sleep(5);
+ exit(1);
+ }
+ }
+
+ log_debug(1, "discovery process to %s:%d exiting",
+ config->address, config->port);
+}
+
+static void
+discovery_file_process(struct iscsi_discovery_process *process)
+{
+ struct iscsi_discovery_file_config *config =
+ process->entry->config.file;
+ struct sigaction action;
+ int fd;
+ int luns_changed = 0;
+ struct string_buffer sendtargets;
+ struct string_buffer info;
+
+ process->pid = getpid();
+ log_debug(1,
+ "discovery file process %d, pid %d, file %s, "
+ "username %s, password %s",
+ process->order, process->pid, config->filename,
+ config->auth_options.username, config->auth_options.password);
+
+ if (!config->filename || !config->filename[0]) {
+ log_error("no discovery filename specified");
+ exit(0);
+ }
+
+ /* set SIGTERM, SIGINT, and SIGPIPE to kill the process */
+ memset(&action, 0, sizeof (struct sigaction));
+ action.sa_sigaction = NULL;
+ action.sa_flags = 0;
+ action.sa_handler = SIG_DFL;
+ sigaction(SIGTERM, &action, NULL);
+ sigaction(SIGINT, &action, NULL);
+ sigaction(SIGPIPE, &action, NULL);
+
+ /* set SIGHUP to loop again and report LUN INVENTORY CHANGED for testing
+ */
+ action.sa_handler = sighup_handler;
+ sigaction(SIGHUP, &action, NULL);
+
+ /* allocate data buffers for SendTargets data and pipe info */
+ init_string_buffer(&sendtargets, 32 * 1024);
+ init_string_buffer(&info, 8 * 1024);
+
+repeat:
+ truncate_buffer(&sendtargets, 0);
+ truncate_buffer(&info, 0);
+
+ if ((fd = open(config->filename, 0))) {
+ char *data = buffer_data(&sendtargets);
+ int rc;
+ int final = 0;
+
+ log_debug(4,
+ "discovery process %p pid %d opened discovery file %s",
+ process, process->pid, config->filename);
+
+ do {
+ char *start =
+ buffer_data(&sendtargets) +
+ data_length(&sendtargets);
+ char *end = start + config->read_size;
+
+ /* don't overflow the buffer */
+ if (end > (start + unused_length(&sendtargets)))
+ end = start + unused_length(&sendtargets);
+
+ data = start;
+
+ /* read a chunk of data */
+ do {
+ rc = read(fd, data, end - data);
+ if (rc > 0) {
+ data += rc;
+ enlarge_data(&sendtargets, rc);
+ log_debug(7,
+ "discovery process %p pid %d "
+ "read %d bytes from %s",
+ process, process->pid, rc,
+ config->filename);
+ } else if (rc < 0) {
+ log_error
+ ("discovery process %p pid %d "
+ "error reading discovery file %s",
+ process, process->pid,
+ config->filename);
+ final = 1;
+ break;
+ } else {
+ log_debug(4,
+ "discovery process %p pid %d "
+ "read to the end of "
+ "discovery file %s",
+ process, process->pid,
+ config->filename);
+ end = data;
+ final = 1;
+ break;
+ }
+ } while (data < end);
+
+ /* convert all the whitespace to NULs */
+ for (data = start; data < end; data++) {
+ if ((*data == '\n') || (*data == ' ')
+ || (*data == '\t'))
+ *data = '\0';
+ }
+
+ /* process the data */
+ process_sendtargets_response(&sendtargets, &info, final,
+ luns_changed,
+ config->address,
+ config->port,
+ process->pipe_fd);
+
+ } while (!final);
+ } else {
+ log_error("discovery process %p pid %d couldn't open "
+ "discovery file %s", process, process->pid,
+ config->filename);
+ }
+
+ while (config->continuous) {
+ /* wait for a signal, then repeat */
+ luns_changed = 1;
+ log_debug(7, "discovery file process waiting for signals");
+ poll(NULL, 0, -1);
+ if (rediscover) {
+ rediscover = 0;
+ goto repeat;
+ }
+ }
+
+ exit(0);
+}
+
+#ifdef SLP_ENABLE
+
+void
+slp_discovery_process(struct iscsi_discovery_process *discovery)
+{
+ struct iscsi_slp_config *config = discovery->entry->config.slp;
+ struct sigaction action;
+ char *pl;
+ unsigned short flag = 0;
+
+ memset(&action, 0, sizeof (struct sigaction));
+ action.sa_sigaction = NULL;
+ action.sa_flags = 0;
+ action.sa_handler = SIG_DFL;
+ sigaction(SIGTERM, &action, NULL);
+ sigaction(SIGINT, &action, NULL);
+ sigaction(SIGPIPE, &action, NULL);
+
+ action.sa_handler = sighup_handler;
+ sigaction(SIGHUP, &action, NULL);
+
+ if (iscsi_process_should_exit()) {
+ log_debug(1, "slp discovery process %p exiting\n", discovery);
+ exit(0);
+ }
+
+ discovery->pid = getpid();
+
+ pl = generate_predicate_list(discovery, &flag);
+
+ while (1) {
+ if (flag == SLP_MULTICAST_ENABLED) {
+ discovery->flag = SLP_MULTICAST_ENABLED;
+ slp_multicast_srv_query(discovery, pl, GENERIC_QUERY);
+ }
+
+ if (flag == SLP_UNICAST_ENABLED) {
+ discovery->flag = SLP_UNICAST_ENABLED;
+ slp_unicast_srv_query(discovery, pl, GENERIC_QUERY);
+ }
+
+ sleep(config->poll_interval);
+ }
+
+ exit(0);
+}
+
+#endif
+
+void
+discovery_process(struct iscsi_discovery_process *discovery)
+{
+ if (discovery->entry) {
+ switch (discovery->entry->type) {
+ case CONFIG_TYPE_DISCOVERY_FILE:
+ discovery_file_process(discovery);
+ break;
+ case CONFIG_TYPE_SENDTARGETS:
+ sendtargets_discovery_process(discovery);
+ break;
+ case CONFIG_TYPE_SLP:
+#ifdef SLP_ENABLE
+ slp_discovery_process(discovery);
+#else
+ log_error("this build does not support SLP");
+ exit(0);
+#endif
+ break;
+ default:
+ log_error("can't fork unexpected discovery type %d",
+ discovery->entry->type);
+ break;
+ }
+ }
+}
diff --git a/usr/discovery.h b/usr/discovery.h
new file mode 100644
index 0000000..5a7ed43
--- /dev/null
+++ b/usr/discovery.h
@@ -0,0 +1,17 @@
+#ifndef ISCSI_DISCOVERY_H_
+#define ISCSI_DISCOVERY_H_
+
+#include "iscsid.h" /* for process types */
+#include "string-buffer.h"
+
+/* functions */
+extern void discovery_process(struct iscsi_discovery_process *discovery);
+
+/* functions useful for implementing other types of discovery processes */
+extern int add_target_record(struct string_buffer *info, char *name, char *end,
+ int lun_inventory_changed, char *default_address,
+ char *default_port, int fd);
+extern int add_portal(struct string_buffer *info, char *address, char *port,
+ char *tag);
+
+#endif
diff --git a/usr/initiator.h b/usr/initiator.h
index 41ef343..00c0004 100644
--- a/usr/initiator.h
+++ b/usr/initiator.h
@@ -22,11 +22,12 @@
#include <stdint.h>
+#include "types.h"
#include "iscsi_proto.h"
#include "auth.h"
/* daemon's session structure */
-struct iscsi_session {
+typedef struct iscsi_session {
int socket_fd;
int login_timeout;
int auth_timeout;
@@ -85,8 +86,8 @@ struct iscsi_session {
char username_in[AUTH_STR_MAX_LEN];
uint8_t password_in[AUTH_STR_MAX_LEN];
int password_length_in;
-};
-extern int iscsi_update_address(struct iscsi_session *session, char *address);
+} iscsi_session_t;
+extern int iscsi_update_address(iscsi_session_t *session, char *address);
/* login.c */
@@ -125,13 +126,14 @@ enum iscsi_login_status {
};
/* implemented in iscsi-login.c for use on all platforms */
-extern int iscsi_add_text(struct iscsi_session *session, struct iscsi_hdr *pdu,
+extern int iscsi_add_text(iscsi_session_t *session, iscsi_hdr_t *hdr,
char *data, int max_data_length, char *param,
char *value);
-extern enum iscsi_login_status iscsi_login(struct iscsi_session *session,
+extern enum iscsi_login_status iscsi_login(iscsi_session_t *session,
char *buffer, uint32_t bufsize,
uint8_t * status_class,
uint8_t * status_detail);
+extern int iscsi_update_address(iscsi_session_t *session, char *address);
/* Digest types */
#define ISCSI_DIGEST_NONE 0
@@ -148,4 +150,13 @@ extern enum iscsi_login_status iscsi_login(struct iscsi_session *session,
#define IRRELEVANT_DATAPDUINORDER 0x40
#define IRRELEVANT_DATASEQUENCEINORDER 0x80
+/* io.c */
+extern int iscsi_connect(iscsi_session_t *session);
+extern void iscsi_disconnect(iscsi_session_t *session);
+extern int iscsi_send_pdu(iscsi_session_t *session, iscsi_hdr_t *hdr,
+ int hdr_digest, char *data, int data_digest, int timeout);
+extern int iscsi_recv_pdu(iscsi_session_t *session, iscsi_hdr_t *hdr,
+ int hdr_digest, char *data, int max_data_length, int data_digest,
+ int timeout);
+
#endif /* INITIATOR_H */
diff --git a/usr/io.c b/usr/io.c
index 31986f8..7a64cd8 100644
--- a/usr/io.c
+++ b/usr/io.c
@@ -45,7 +45,7 @@ sigalarm_handler(int unused)
}
int
-iscsi_connect(struct iscsi_session *session)
+iscsi_connect(iscsi_session_t *session)
{
int ret, rc, sock, onearg;
struct sockaddr_in addr;
@@ -161,7 +161,7 @@ iscsi_connect(struct iscsi_session *session)
}
void
-iscsi_disconnect(struct iscsi_session *session)
+iscsi_disconnect(iscsi_session_t *session)
{
if (session->socket_fd >= 0) {
log_debug(1, "disconnecting session %p, fd %d", session,
@@ -172,7 +172,7 @@ iscsi_disconnect(struct iscsi_session *session)
}
static void
-iscsi_log_text(struct iscsi_hdr *pdu, char *data)
+iscsi_log_text(iscsi_hdr_t *pdu, char *data)
{
int dlength = ntoh24(pdu->dlength);
char *text = data;
@@ -187,7 +187,7 @@ iscsi_log_text(struct iscsi_hdr *pdu, char *data)
}
int
-iscsi_send_pdu(struct iscsi_session *session, struct iscsi_hdr *hdr,
+iscsi_send_pdu(iscsi_session_t *session, iscsi_hdr_t *hdr,
int hdr_digest, char *data, int data_digest, int timeout)
{
int rc, ret = 0;
@@ -340,7 +340,7 @@ iscsi_send_pdu(struct iscsi_session *session, struct iscsi_hdr *hdr,
}
int
-iscsi_recv_pdu(struct iscsi_session *session, struct iscsi_hdr *hdr,
+iscsi_recv_pdu(iscsi_session_t *session, iscsi_hdr_t *hdr,
int hdr_digest, char *data, int max_data_length, int data_digest,
int timeout)
{
@@ -357,8 +357,8 @@ iscsi_recv_pdu(struct iscsi_session *session, struct iscsi_hdr *hdr,
struct sigaction action;
struct sigaction old;
- /* set a timeout, since the socket calls may take a long
- * time to timeout on their own
+ /* set a timeout, since the socket calls may take a long
+ * time to timeout on their own
*/
memset(data, 0, max_data_length);
memset(&action, 0, sizeof (struct sigaction));
diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
index 0c46c79..2bcf3e4 100644
--- a/usr/iscsiadm.c
+++ b/usr/iscsiadm.c
@@ -37,6 +37,10 @@
static char program_name[] = "iscsiadm";
+/* global config info */
+struct iscsi_daemon_config daemon_config;
+struct iscsi_daemon_config *dconfig = &daemon_config;
+
static struct option const long_options[] =
{
{"version", no_argument, 0, 'v'},
diff --git a/usr/iscsiadm.h b/usr/iscsiadm.h
index d6b7af5..278c866 100644
--- a/usr/iscsiadm.h
+++ b/usr/iscsiadm.h
@@ -24,16 +24,41 @@
#define ISCSIADM_NAME_LEN 128
typedef enum iscsiadm_cmd {
- C_SESSION_ADD,
- C_SESSION_REMOVE,
+ IPC_SESSION_ADD,
+ IPC_SESSION_REMOVE,
+ IPC_CONN_ADD,
+ IPC_CONN_REMOVE,
} iscsiadm_cmd_e;
+typedef struct msg_session_add {
+ char name[ISCSIADM_NAME_LEN];
+ char alias[ISCSIADM_NAME_LEN];
+} msg_session_add_t;
+
+typedef struct msg_session_rm {
+ int sid;
+} msg_session_rm_t;
+
+typedef struct msg_conn_add {
+ uint8_t ip_address[16];
+ int port;
+} msg_conn_add_t;
+
+typedef struct msg_conn_rm {
+ int sid;
+ int cid;
+} msg_conn_rm_t;
+
/* IPC Request */
typedef struct iscsiadm_req {
iscsiadm_cmd_e command;
union {
/* messages */
+ msg_session_add_t s_add;
+ msg_session_rm_t s_rm;
+ msg_conn_add_t c_add;
+ msg_conn_rm_t c_rm;
} u;
} iscsiadm_req_t;
@@ -46,4 +71,29 @@ int ipc_handle(int accept_fd);
int ipc_listen(void);
void ipc_close(int fd);
+struct iscsi_discovery_process {
+ struct iscsi_discovery_process *volatile prev;
+ struct iscsi_discovery_process *volatile next;
+ struct iscsi_config_entry *entry;
+ pid_t pid;
+ int order;
+ int pipe_fd;
+ int in_progress;
+ int remove; /* kill and remove this from the
+ * list at the next opportunity */
+ int restart; /* restart if the pid is 0 */
+ unsigned short flag; /* UNICAST or MULTICAST */
+};
+
+/* daemon config */
+struct iscsi_daemon_config {
+ char *config_file;
+ char *pid_file;
+ char *initiator_name_file;
+ char *initiator_name;
+ char *initiator_alias;
+};
+
+extern struct iscsi_daemon_config *dconfig;
+
#endif /* ISCSIADM_H */
diff --git a/usr/iscsid.c b/usr/iscsid.c
index a9bb8c3..7581f37 100644
--- a/usr/iscsid.c
+++ b/usr/iscsid.c
@@ -77,7 +77,7 @@ CONFIG_FILE ").\n\
}
void
-handle_iscsi_events(void)
+iscsi_events_handle(void)
{
iscsi_uevent_t event;
int res;
@@ -132,11 +132,10 @@ event_loop(void)
}
if (poll_array[POLL_CTRL].revents)
- handle_iscsi_events();
+ iscsi_events_handle();
if (poll_array[POLL_IPC].revents)
ipc_handle(ipc_fd);
-
}
}
diff --git a/usr/iscsid.h b/usr/iscsid.h
index 3af55b3..da795e8 100644
--- a/usr/iscsid.h
+++ b/usr/iscsid.h
@@ -23,8 +23,7 @@
#include <sys/types.h>
#include <sys/ioctl.h>
-#include "types.h"
-#include "iscsi_proto.h"
+#include "initiator.h"
#define BHS_SIZE 48
@@ -36,15 +35,17 @@ typedef struct iscsi_pdu {
unsigned int datasize;
} iscsi_pdu_t;
-/* ctldev.c */
-extern int ctrl_fd;
-
-extern int ctldev_open(void);
-
#define version() \
do { \
printf("%s version %s\n", program_name, ISCSI_VERSION_STR); \
exit(0); \
} while (0)
+/* ctldev.c */
+extern int ctrl_fd;
+
+extern int ctldev_open(void);
+extern void ctldev_close(int fd);
+
+
#endif /* ISCSID_H */
diff --git a/usr/login.c b/usr/login.c
index dc3665a..a39bac7 100644
--- a/usr/login.c
+++ b/usr/login.c
@@ -34,7 +34,7 @@
/* caller is assumed to be well-behaved and passing NUL terminated strings */
int
-iscsi_add_text(struct iscsi_session *session, struct iscsi_hdr *pdu, char *data,
+iscsi_add_text(iscsi_session_t *session, iscsi_hdr_t *pdu, char *data,
int max_data_length, char *param, char *value)
{
int param_len = strlen(param);
@@ -156,12 +156,12 @@ get_auth_key_type(struct iscsi_acl *auth_client, char **data, char *end)
return LOGIN_NEGOTIATION_FAILED;
}
-/*
- * try to reset the session's IP address and port, based on the TargetAddress
- * provided
+/*
+ * try to reset the session's IP address and port, based on the TargetAddress
+ * provided
*/
int
-iscsi_update_address(struct iscsi_session *session, char *address)
+iscsi_update_address(iscsi_session_t *session, char *address)
{
char *port;
char *tag;
@@ -196,7 +196,7 @@ iscsi_update_address(struct iscsi_session *session, char *address)
}
static enum iscsi_login_status
-get_security_text_keys(struct iscsi_session *session, char **data,
+get_security_text_keys(iscsi_session_t *session, char **data,
struct iscsi_acl *auth_client, char *end)
{
char *text = *data;
@@ -277,7 +277,7 @@ get_security_text_keys(struct iscsi_session *session, char **data,
}
static enum iscsi_login_status
-get_op_params_text_keys(struct iscsi_session *session, char **data, char *end)
+get_op_params_text_keys(iscsi_session_t *session, char **data, char *end)
{
char *text = *data;
char *value = NULL;
@@ -547,7 +547,7 @@ get_op_params_text_keys(struct iscsi_session *session, char **data, char *end)
}
static enum iscsi_login_status
-check_security_stage_status(struct iscsi_session *session,
+check_security_stage_status(iscsi_session_t *session,
struct iscsi_acl *auth_client)
{
int debug_status = 0;
@@ -588,7 +588,7 @@ check_security_stage_status(struct iscsi_session *session,
* size, and then appending a NULL to the PDU.
*/
static enum iscsi_login_status
-iscsi_process_login_response(struct iscsi_session *session,
+iscsi_process_login_response(iscsi_session_t *session,
iscsi_login_rsp_t *login_rsp,
char *data, int max_data_length)
{
@@ -721,7 +721,7 @@ iscsi_process_login_response(struct iscsi_session *session,
}
static int
-add_params_normal_session(struct iscsi_session *session, struct iscsi_hdr *pdu,
+add_params_normal_session(iscsi_session_t *session, iscsi_hdr_t *pdu,
char *data, int max_data_length)
{
char value[AUTH_STR_MAX_LEN];
@@ -764,7 +764,7 @@ add_params_normal_session(struct iscsi_session *session, struct iscsi_hdr *pdu,
}
static int
-add_vendor_specific_text(struct iscsi_session *session, struct iscsi_hdr *pdu,
+add_vendor_specific_text(iscsi_session_t *session, iscsi_hdr_t *pdu,
char *data, int max_data_length)
{
char value[AUTH_STR_MAX_LEN];
@@ -813,7 +813,7 @@ add_vendor_specific_text(struct iscsi_session *session, struct iscsi_hdr *pdu,
}
static int
-check_irrelevant_keys(struct iscsi_session *session, struct iscsi_hdr *pdu,
+check_irrelevant_keys(iscsi_session_t *session, iscsi_hdr_t *pdu,
char *data, int max_data_length)
{
/* If you receive irrelevant keys, just check them from the irrelevant
@@ -864,7 +864,7 @@ check_irrelevant_keys(struct iscsi_session *session, struct iscsi_hdr *pdu,
}
static int
-fill_crc_digest_text(struct iscsi_session *session, struct iscsi_hdr *pdu,
+fill_crc_digest_text(iscsi_session_t *session, iscsi_hdr_t *pdu,
char *data, int max_data_length)
{
switch (session->header_digest) {
@@ -918,7 +918,7 @@ fill_crc_digest_text(struct iscsi_session *session, struct iscsi_hdr *pdu,
}
static int
-fill_op_params_text(struct iscsi_session *session, struct iscsi_hdr *pdu,
+fill_op_params_text(iscsi_session_t *session, iscsi_hdr_t *pdu,
char *data, int max_data_length, int *transit)
{
char value[AUTH_STR_MAX_LEN];
@@ -989,7 +989,7 @@ fill_op_params_text(struct iscsi_session *session, struct iscsi_hdr *pdu,
}
static void
-enum_auth_keys(struct iscsi_acl *auth_client, struct iscsi_hdr *pdu,
+enum_auth_keys(struct iscsi_acl *auth_client, iscsi_hdr_t *pdu,
char *data, int max_data_length, int keytype)
{
int present = 0, rc;
@@ -1022,7 +1022,7 @@ enum_auth_keys(struct iscsi_acl *auth_client, struct iscsi_hdr *pdu,
}
static int
-fill_security_params_text(struct iscsi_session *session, struct iscsi_hdr *pdu,
+fill_security_params_text(iscsi_session_t *session, iscsi_hdr_t *pdu,
struct iscsi_acl *auth_client, char *data,
int max_data_length, int *transit)
{
@@ -1075,7 +1075,7 @@ fill_security_params_text(struct iscsi_session *session, struct iscsi_hdr *pdu,
*
**/
static int
-iscsi_make_login_pdu(struct iscsi_session *session, struct iscsi_hdr *hdr,
+iscsi_make_login_pdu(iscsi_session_t *session, iscsi_hdr_t *hdr,
char *data, int max_data_length)
{
int transit = 0;
@@ -1092,7 +1092,7 @@ iscsi_make_login_pdu(struct iscsi_session *session, struct iscsi_hdr *hdr,
login_hdr->cid = 0;
memcpy(login_hdr->isid, session->isid, sizeof(session->isid));
login_hdr->tsih = 0;
- login_hdr->cmdsn = htonl(session->cmdsn);
+ login_hdr->cmdsn = htonl(session->cmdsn);
/* don't increment on immediate */
login_hdr->min_version = ISCSI_DRAFT20_VERSION;
login_hdr->max_version = ISCSI_DRAFT20_VERSION;
@@ -1185,7 +1185,7 @@ iscsi_make_login_pdu(struct iscsi_session *session, struct iscsi_hdr *hdr,
}
static enum iscsi_login_status
-check_for_authentication(struct iscsi_session *session,
+check_for_authentication(iscsi_session_t *session,
struct iscsi_acl *auth_client)
{
enum iscsi_login_status ret = LOGIN_FAILED;
@@ -1199,7 +1199,7 @@ check_for_authentication(struct iscsi_session *session,
return LOGIN_FAILED;
}
- if (session->username &&
+ if (session->username &&
(acl_set_user_name(auth_client, session->username) !=
AUTH_STATUS_NO_ERROR)) {
log_error("Couldn't set username\n");
@@ -1236,7 +1236,7 @@ check_for_authentication(struct iscsi_session *session,
}
static enum iscsi_login_status
-check_status_login_response(struct iscsi_session *session,
+check_status_login_response(iscsi_session_t *session,
iscsi_login_rsp_t *login_rsp,
char *data, int max_data_length, int *final)
{
@@ -1300,11 +1300,11 @@ check_status_login_response(struct iscsi_session *session,
* that we don't have any policy logic here.
**/
enum iscsi_login_status
-iscsi_login(struct iscsi_session *session, char *buffer, size_t bufsize,
+iscsi_login(iscsi_session_t *session, char *buffer, size_t bufsize,
uint8_t * status_class, uint8_t * status_detail)
{
struct iscsi_acl *auth_client = NULL;
- struct iscsi_hdr pdu;
+ iscsi_hdr_t pdu;
iscsi_login_rsp_t *login_rsp =
(iscsi_login_rsp_t *)&pdu;
char *data;
@@ -1362,8 +1362,7 @@ iscsi_login(struct iscsi_session *session, char *buffer, size_t bufsize,
*/
if (!iscsi_make_login_pdu(session, &pdu, data,
max_data_length)) {
- log_error("login failed, couldn't make "
- "a login PDU\n");
+ log_error("login failed, couldn't make a login PDU\n");
ret = LOGIN_FAILED;
goto done;
}
@@ -1374,11 +1373,9 @@ iscsi_login(struct iscsi_session *session, char *buffer, size_t bufsize,
/*
* FIXME: caller might want us to distinguish I/O
* error and timeout. Might want to switch portals on
- * timeouts, but
- * not I/O errors.
+ * timeouts, but not I/O errors.
*/
- log_error("Login I/O error, failed to "
- "send a PDU\n");
+ log_error("Login I/O error, failed to send a PDU\n");
ret = LOGIN_IO_ERROR;
goto done;
}
@@ -1392,8 +1389,7 @@ iscsi_login(struct iscsi_session *session, char *buffer, size_t bufsize,
* error and timeout. Might want to switch portals on
* timeouts, but not I/O errors.
*/
- log_error("Login I/O error, failed to "
- "receive a PDU\n");
+ log_error("Login I/O error, failed to receive a PDU\n");
ret = LOGIN_IO_ERROR;
goto done;
}
diff --git a/usr/md5.h b/usr/md5.h
index cdbbe2c..100eecc 100644
--- a/usr/md5.h
+++ b/usr/md5.h
@@ -24,7 +24,7 @@
#define MD5_H
#include <string.h>
-#include <sys/types.h>
+#include <sys/types.h>
#include <netinet/in.h>
#include <stdint.h>
#if (__BYTE_ORDER == __BIG_ENDIAN)
diff --git a/usr/strings.c b/usr/strings.c
new file mode 100644
index 0000000..a3d4a23
--- /dev/null
+++ b/usr/strings.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2002 Cisco Systems, Inc.
+ * maintained by linux-iscsi-devel@lists.sourceforge.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * See the file COPYING included with this distribution for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/param.h>
+
+#include "strings.h"
+#include "log.h"
+
+int
+init_string_buffer(struct string_buffer *s, size_t initial_allocation)
+{
+ if (s) {
+ memset(s, 0, sizeof (*s));
+ s->buffer = NULL;
+ if (initial_allocation) {
+ s->buffer = malloc(initial_allocation);
+ if (s->buffer) {
+ s->allocated_length = initial_allocation;
+ memset(s->buffer, 0, initial_allocation);
+ }
+ }
+ s->data_length = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+struct string_buffer *
+alloc_string_buffer(size_t initial_allocation)
+{
+ struct string_buffer *s = calloc(1, sizeof (*s));
+
+ if (s) {
+ init_string_buffer(s, initial_allocation);
+ }
+
+ return s;
+}
+
+void
+free_string_buffer(struct string_buffer *s)
+{
+ if (s) {
+ if (s->buffer) {
+ free(s->buffer);
+ s->buffer = NULL;
+ }
+ s->allocated_length = 0;
+ s->data_length = 0;
+ free(s);
+ }
+}
+
+void
+enlarge_data(struct string_buffer *s, int length)
+{
+ if (s) {
+ s->data_length += length;
+ if (s->data_length >= s->allocated_length) {
+ /* too big */
+ log_error(
+ "enlarged buffer %p to %d data bytes, "
+ "with only %d bytes of buffer space",
+ s, s->data_length, s->allocated_length);
+ }
+ }
+}
+
+void
+remove_initial(struct string_buffer *s, int length)
+{
+ char *remaining = s->buffer + length;
+ int amount = s->data_length - length;
+
+ if (s && length) {
+ memmove(s->buffer, remaining, amount);
+ s->data_length = amount;
+ s->buffer[amount] = '\0';
+ }
+}
+
+static int
+realloc_buffer(struct string_buffer *s, size_t min_length)
+{
+ size_t length = MAX(min_length + 1, s->allocated_length + 1024);
+ char *buf = realloc(s->buffer, length);
+
+ if (buf) {
+ s->buffer = buf;
+ s->buffer[length - 1] = '\0';
+ s->allocated_length = length;
+ return 1;
+ } else {
+ log_error(
+ "failed to allocate more space for string buffer %p", s);
+ return 0;
+ }
+}
+
+/* truncate the data length down */
+void
+truncate_buffer(struct string_buffer *s, size_t length)
+{
+ if (s) {
+ if (length <= s->data_length) {
+ s->data_length = length;
+ s->buffer[s->data_length] = '\0';
+ } else if (length <= s->allocated_length) {
+ /* clear the data, and declare the
+ * data length to be larger
+ */
+ memset(s->buffer + s->data_length, 0,
+ length - s->data_length);
+ s->data_length = length;
+ } else {
+ log_error(
+ "couldn't truncate data buffer to length %d, "
+ "only allocated %d",
+ length, s->allocated_length);
+ }
+ }
+}
+
+/* append a string onto the buffer */
+int
+append_string(struct string_buffer *s, const char *str)
+{
+ size_t length = strlen(str);
+ size_t needed = s->data_length + length + 1; /* existing + new +
+ * trailing NUL
+ */
+
+ if (needed >= s->allocated_length) {
+ /* need more space */
+ if (!realloc_buffer(s, needed))
+ return 0;
+ }
+
+ strcpy(s->buffer + s->data_length, str);
+ s->data_length += length;
+ return 1;
+}
+
+int
+append_sprintf(struct string_buffer *s, const char *format, ...)
+{
+ va_list args;
+ size_t appended;
+ size_t available = s->allocated_length - s->data_length - 1;
+ int ret = 0;
+
+ va_start(args, format);
+
+ for (;;) {
+ appended = vsnprintf(s->buffer + s->data_length, available,
+ format, args);
+
+ if (appended < 0) {
+ /* error, need more space, but don't know how much */
+ if (!realloc_buffer(s, s->data_length + 1024))
+ goto done;
+ } else if (appended >= available) {
+ /* what would have been output overflows the buffer,
+ * need more space
+ */
+ if (!realloc_buffer(s, s->data_length + appended))
+ goto done;
+ } else {
+ /* it fit */
+ s->data_length += appended;
+ ret = 1;
+ break;
+ }
+ }
+
+ done:
+ va_end(args);
+
+ return ret;
+}
+
+/* append a string after the NUL at the end of any current data. This
+ * maintains NUL termination of all strings
+ */
+int
+adjoin_string(struct string_buffer *s, const char *str)
+{
+ size_t length = strlen(str) + 1;
+ size_t needed;
+
+ if (s->buffer[s->data_length - 1] == '\0')
+ needed = s->data_length + length; /* lengths include NULs
+ */
+ else
+ needed = s->data_length + 1 + length; /* existing + NUL +
+ * new + NUL
+ */
+ if (needed >= s->allocated_length) {
+ /* need more space */
+ if (!realloc_buffer(s, needed))
+ return 0;
+ }
+
+ if (s->buffer[s->data_length - 1] == '\0') {
+ memcpy(s->buffer + s->data_length, str, length);
+ /* lengths already include NULs
+ */
+ s->data_length += length; /* new string + NUL */
+ } else {
+ memcpy(s->buffer + s->data_length + 1, str, length);
+ /* NUL + new + NUL */
+ s->data_length += 1 + length; /* NUL + new string + NUL */
+ }
+
+ return 1;
+}
+
+char *
+buffer_data(struct string_buffer *s)
+{
+ if (s)
+ return s->buffer;
+ else
+ return NULL;
+}
+
+size_t
+data_length(struct string_buffer * s)
+{
+ if (s)
+ return s->data_length;
+ else
+ return 0;
+}
+
+size_t
+unused_length(struct string_buffer * s)
+{
+ if (s)
+ return s->allocated_length - s->data_length;
+ else
+ return 0;
+}
+
+/* write the entire buffer to the fd, or exit */
+void
+write_buffer(struct string_buffer *s, int fd)
+{
+ const char *data = buffer_data(s);
+ const char *end = data + data_length(s);
+ int result;
+
+ /* write the target info to the pipe */
+ log_debug(7, "writing to pipe %d, data %p, size %u, text '%s'",
+ fd, data, end - data, data);
+ while (data < end) {
+ result = write(fd, data, end - data);
+ if (result < 0) {
+ if (errno != EINTR) {
+ log_error("can't write to pipe %d", fd);
+ exit(1);
+ }
+ } else {
+ data += result;
+ }
+ }
+}
diff --git a/usr/strings.h b/usr/strings.h
new file mode 100644
index 0000000..a209a44
--- /dev/null
+++ b/usr/strings.h
@@ -0,0 +1,46 @@
+/*
+ * iSCSI variable-sized string buffers
+ *
+ * Copyright (C) 2002 Cisco Systems, Inc.
+ * maintained by linux-iscsi-devel@lists.sourceforge.net
+ *
+ * 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 STRINGS_H
+#define STRINGS_H
+
+struct string_buffer {
+ size_t allocated_length;
+ size_t data_length; /* not including the trailing NUL */
+ char *buffer;
+};
+
+extern int init_string_buffer(struct string_buffer *s,
+ size_t initial_allocation);
+extern struct string_buffer *alloc_string_buffer(size_t initial_allocation);
+extern void free_string_buffer(struct string_buffer *s);
+
+extern void enlarge_data(struct string_buffer *s, int length);
+extern void remove_initial(struct string_buffer *s, int length);
+extern void truncate_buffer(struct string_buffer *s, size_t length);
+extern int append_string(struct string_buffer *s, const char *str);
+extern int append_sprintf(struct string_buffer *s, const char *format, ...);
+extern int adjoin_string(struct string_buffer *s, const char *str);
+extern char *buffer_data(struct string_buffer *s);
+extern size_t data_length(struct string_buffer *s);
+extern size_t unused_length(struct string_buffer *s);
+extern void write_buffer(struct string_buffer *s, int fd);
+
+#endif /* STRINGS_H */