Sysfs interface for targets made uniform. ISCSI-SCST made confirming the uniformity...
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Wed, 17 Feb 2010 19:07:54 +0000 (19:07 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Wed, 17 Feb 2010 19:07:54 +0000 (19:07 +0000)
git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@1511 d57e44dd-8a1f-0410-8b47-8ef2f437770f

51 files changed:
iscsi-scst/ChangeLog-IET [deleted file]
iscsi-scst/README
iscsi-scst/README-IET [deleted file]
iscsi-scst/README_in-tree
iscsi-scst/ToDo
iscsi-scst/include/iscsi_scst.h
iscsi-scst/include/iscsi_scst_ver.h
iscsi-scst/kernel/config.c
iscsi-scst/kernel/conn.c
iscsi-scst/kernel/event.c
iscsi-scst/kernel/iscsi.c
iscsi-scst/kernel/iscsi.h
iscsi-scst/kernel/nthread.c
iscsi-scst/kernel/param.c
iscsi-scst/kernel/session.c
iscsi-scst/kernel/target.c
iscsi-scst/usr/Makefile
iscsi-scst/usr/chap.c
iscsi-scst/usr/config.c
iscsi-scst/usr/conn.c
iscsi-scst/usr/ctldev.c
iscsi-scst/usr/event.c
iscsi-scst/usr/iscsi_adm.c
iscsi-scst/usr/iscsi_adm.h
iscsi-scst/usr/iscsi_scstd.c
iscsi-scst/usr/iscsid.c
iscsi-scst/usr/iscsid.h
iscsi-scst/usr/isns.c
iscsi-scst/usr/log.c
iscsi-scst/usr/message.c
iscsi-scst/usr/param.c
iscsi-scst/usr/param.h
iscsi-scst/usr/session.c
iscsi-scst/usr/target.c
qla2x00t/qla2x00-target/README
qla2x00t/qla2x00-target/qla2x00t.c
scst/README
scst/include/scst.h
scst/include/scst_const.h
scst/include/scst_user.h
scst/src/dev_handlers/scst_user.c
scst/src/dev_handlers/scst_vdisk.c
scst/src/scst_lib.c
scst/src/scst_main.c
scst/src/scst_mem.c
scst/src/scst_proc.c
scst/src/scst_sysfs.c
scst/src/scst_targ.c
usr/fileio/README
usr/fileio/common.h
usr/fileio/fileio.c

diff --git a/iscsi-scst/ChangeLog-IET b/iscsi-scst/ChangeLog-IET
deleted file mode 100644 (file)
index 5433baa..0000000
+++ /dev/null
@@ -1,461 +0,0 @@
-Summary of changes from v0.4.15 to v0.4.16
-=================================
-
-Arne Redlich
-  o fix overzealous assert() in digest_data()
-  o add checking on return value of ISCSI_PARAM_GET
-  o 2.6.22, 2.6.23 and 2.6.24 compile fixes
-  o add conn->rwsize check
-  o avoid potential NULL-ptr dereferences in rx and tx buffer 
-  o fix the shell syntax in init scripts
-
-Dave Jiang
-  o fix digest endieness on LE archs
-
-FUJITA Tomonori
-  o fix SPARC alignment issues (based on a patch from joel.bertrand@systella.fr)
-
-Ross S. W. Walker
-  o fix DISTDIR in Makefile for /etc install
-  o add support to nullio for volumes > 2TB
-  o remove init.d memory size adjustment
-  o add error code reporting to blockio_open_path
-  o blockio gen_scsiid bug fix
-  o add verbosity to kernel output and task management
-
-
-Summary of changes from v0.4.14 to v0.4.15
-=================================
-
-Juhani Rautiainen
- o Add RELEASE/RESERVE support
-
-Ross S. W. Walker
- o Improve the build system to support several kernel versions
- o Add block-io support
-
-
-Summary of changes from v0.4.13 to v0.4.14
-=================================
-
-Arne Redlich
-  o Kill unused "state" in struct iscsi_cmnd.
-  o Fixed fileio_sync() to propagate error to the caller (and initiator).
-  o Don't attempt to show target/session params if communication with ietd
-  fails.
-  o Fixes to ietadm parameters handling.
-
-FUJITA Tomonori
-  o rewritten iSNS code, many iSNS fixes.
-  o added iSNS SCN support.
-  o IPv6 fixes to userspace.
-  
-Ming Zhang
-  o Fix the READ_* commands error handling bug.
-  o fix the mode sense response.
-  o wrong #endif sequence in misc.h
-
-Richard Bollinger
-  o add a patch to ietd.c that allows defunct sessions to go away.
-  o add write-back cache and read-only support.
-
-Frederic Temporelli
-  o Fix for the combination of 32-bit userland and 64-bit kernel on mips.
-
-Henry Liu
-  o corrected many task management functions, prevent crashing on
-  LUN RESET, TARGET WARM RESET.
-
-K Chapman
-  o Fixed a typo in check_segment_length().
-
-Emmanuel Florac
-  o Add ietadm manpage.
-
-
-Summary of changes from v0.4.12 to v0.4.13
-=================================
-Arne Redlich
-  o patch to avoid digest calculation for PDUs whose data has been skipped 
-  already for various reasons.
-  o Correct a bug managing non-default MaxRxDSL.
-  o added to ietadm ability to show target parameters.
-  o add on the workaround to AIX initiator MaxCmdSN bug.
-
-FUJITA Tomonori
-  o added to ietadm ability to show the iSCSI parameters for an established 
-  session.
-
-Ming Zhang
-  o Fixed this bug : ietd should manage the iscsi name in a case insensitive
-  way to conform to the RFC.
-  o workaround to AIX initiator MaxCmdSN bug.
-  o Fixed socket() return value judgment.
-  
-Bastiaan Bakker
-  o add 'condrestart' command to the RedHat initscript.
-
-Robert Whitehead
-  o correct the bug that prevents iet to start if there isn't 
-  an /etc/ietd.conf file.
-  
-Summary of changes from v0.4.11 to v0.4.12
-=================================
-
-Arne Redlich
-  o Fix MaxRecvDataSegmentLength handling.
-  o Fix login parameter handling.
-  o Update man pages.
-
-Bastiaan Bakker
-  o Add features to specify the listen address and port.
-  o Fix setuid and setgid bugs in ietd daemon.
-
-FUJITA Tomonori
-  o Add IPv6 support.
-
-Junjiro Okajima
-  o Fix a bug about getting parameters from kernel space.
-
-Krzysztof Blaszkowski
-  o Fix for requests with unaligned to 4 length.
-
-
-Summary of changes from v0.4.10 to v0.4.11
-=================================
-
-FUJITA Tomonori
-  o Fix Task Management Function bugs.
-
-Ming Zhang
-  o Update man pages.
-
-
-Summary of changes from v0.4.9 to v0.4.10
-=================================
-
-Arne Redlich
-  o Fix 0x83 inquiry output.
-  o Fix iSCSI parameter handling bugs.
-
-FUJITA Tomonori
-  o Add the access control based on initiator address
-    and target name patterns.
-
-Junjiro Okajima
-  o Fix parameter checking bugs.
-
-Ming Zhang
-  o Add the nullio mode (only useful for performance evaluation).
-
-
-Summary of changes from v0.4.8 to v0.4.9
-=================================
-
-FUJITA Tomonori
-  o Fix parameter negotiation handling bugs.
-
-Wang Zhenyu
-  o Fix digest negotiation handling bugs.
-
-
-Summary of changes from v0.4.7 to v0.4.8
-=================================
-
-FUJITA Tomonori
-  o Fix unsolicited data handling bugs.
-  o Rewrite parameter handling code.
-  o Rewrite ietadm tool.
-  o Improve dynamic configuration support.
-  o Cleanups on the kernel-user interface.
-  o Improve wrong PDU handling.
-  o Implement a framework to handle multiple configuration methods.
-  o Implement basic access control support.
-
-
-Summary of changes from v0.4.6 to v0.4.7
-=================================
-
-Florian Zierer
-  o Add the startup script for Gentoo.
-
-FUJITA Tomonori
-  o Rewrite parameter handling code.
-  o Fix task management code bug.
-  o Fix 0x83 inquiry output (Thanks to Christophe Varoqui).
-
-Ming Zhang
-  o Acquire T10 ID.
-  o Fix parameter handling bugs.
-  o Some user-space cleanups.
-
-Philipp Hug
-  o Fix ietd.8 man typo.
-
-
-Summary of changes from v0.4.5 to v0.4.6
-=================================
-
-FUJITA Tomonori
-  o Replace the makeshift event notification code with netlink.
-  o Add task management code except for ACA and reassign stuff.
-  o Fix r2t lun bug (Thanks to Ming Zhang).
-
-
-Summary of changes from v0.4.4 to v0.4.5
-=================================
-
-FUJITA Tomonori
-  o Rewrite the iSCSI command handling code.
-  o Rewrite the I/O data handling code.
-  o Fix worker thread.
-  o Several cleanups.
-
-
-Summary of changes from v0.4.3 to v0.4.4
-=================================
-
-Krzysztof Blaszkowski
-  o Fix an out-of-memory bug.
-
-
-Summary of changes from v0.4.2 to v0.4.3
-=================================
-
-Arne Redlich
-  o Fix header digest bug.
-  o Fix unexpected closed connection bug.
-  o Fix iSCSI parameter bug.
-
-FUJITA Tomonori
-  o Fix network thread.
-
-
-Summary of changes from v0.4.1 to v0.4.2
-=================================
-
-FUJITA Tomonori
-  o Fix network thread.
-  o Fix MaxOutstandingR2T handling.
-
-Ming Zhang
-  o Add large volume support (over 2TB).
-
-
-Summary of changes from v0.4.0 to v0.4.1
-=================================
-
-Arne Redlich
-  o Add mutual CHAP support. Note that you need to replace "User"
-    with "IncomingUser" in ietd.conf.
-
-FUJITA Tomonori
-  o Fix InitialR2T=No support.
-  o Fix INQUIRY command handling.
-  o Fix network and worker thread.
-  o Start to split SCSI stuff.
-  o Rewrite the R2T handling code.
-  o Several cleanups.
-
-
-Summary of changes from v0.3.8 to v0.4.0
-=================================
-       
-Arne Redlich
-  o iSNS bug fix.
-
-FUJITA Tomonori
-  o Move to 2.6 kernels.
-  o Rewrite the kernel thread performing network I/O.
-  o Add header and data digests (Thanks to Arne Redlich).
-
-Ming Zhang
-  o Add mode sense page 0x3 and 0x4 support (Thanks to K Chapman).
-  o iSNS bug fix.
-
-
-Summary of changes from v0.3.7 to v0.3.8
-=================================
-       
-Arne Redlich
-  o Fix ietadm global option bug.
-
-FUJITA Tomonori
-  o Fix TCP option bugs (Thanks to Chuck Berg).
-  o Fix REPORT LUN (handling lots of LUs).
-
-
-Summary of changes from v0.3.6 to v0.3.7
-=================================
-       
-Arne Redlich
-  o Fix target_alloc_pages().
-
-FUJITA Tomonori
-  o Fix REPORT LUN bug.
-
-
-Summary of changes from v0.3.5 to v0.3.6
-=================================
-       
-Arne Redlich
-  o Fix bugs about rejecting PDUs.
-
-FUJITA Tomonori
-  o Cleanups on target_cmnd structure.
-  o Kill highmem stuff.
-  o Fix REPORT LUN (handling lots of LUs).
-
-
-Summary of changes from v0.3.4 to v0.3.5
-=================================
-       
-Arne Redlich
-  o Fix ietd security hole.
-  o Fix REPORT LUN bug.
-  o FIX NOOP_OUT padding bug.
-
-FUJITA Tomonori
-  o Rewrite event notification code.
-
-Libor Vanek
-  o Add max_sessions option.
-  o Fix command parsing bug.
-
-Ming Zhang
-  o Cleanups for 64-bit architectures.
-
-
-Summary of changes from v0.3.3 to v0.3.4
-=================================
-
-FUJITA Tomonori
-  o Improve dynamic configuration support (adding targets and users).
-
-
-Summary of changes from v0.3.2 to v0.3.3
-=================================
-
-FUJITA Tomonori
-  o Fix Makefile for the startup script.
-
-
-Summary of changes from v0.3.1 to v0.3.2
-=================================
-
-Ali Lehmann
-  o Add a new startup script for Debian.
-
-FUJITA Tomonori
-  o Fix the istd's handling of connections in out-of-memory situations.
-  o Fix bugs in regular file support.
-  o Fix `ietadm --mode del all`.
-
-Libor Vanek
-  o Add a new startup script for RedHat.
-
-Ming Zhang
-  o Add uid/gid option to ietd daemon.
-  o Fix a access freed-memory bug in kernel/daemon.c.
-
-
-Summary of changes from v0.3.0 to v0.3.1
-=================================
-
-FUJITA Tomonori
-  o Fix memory leaks in ietd daemon (Thanks to Ming).
-  o Fix bugs about REPORT_LUNS commands (Thanks to Ming).
-  o Fix a bug about Target Task Tag.
-  o Add regular file support to fileio mode.
-
-
-Summary of changes from v0.2.6 to v0.3.0
-=================================
-
-Ali Lehmann
-  o Update ietd.8 man page.
-
-FUJITA Tomonori
-  o Fix shutdown code.
-  o Fix istd kernel thread bugs.
-  o Replace procfs interface with ioctl.
-  o Add dynamic configuration support.
-  o Update README and the boot script.
-
-Ming Zhang
-  o Add config option to ietd daemon.
-
-
-Summary of changes from v0.2.5 to v0.2.6
-=================================
-
-Ali Lehmann
-  o Add ietd.8 and ietd.conf.5 man pages.
-
-FUJITA Tomonori
-  o Update README, Makefile, and the boot script.
-
-
-Summary of changes from v0.2.4 to v0.2.5
-=================================
-
-FUJITA Tomonori
-  o Update README.
-
-
-Summary of changes from v0.2.3 to v0.2.4
-=================================
-
-Ming Zhang
-  o Add a preliminary iSNS support.
-  o Fix merge mistakes that I made at the previous release.
-
-
-Summary of changes from v0.2.2 to v0.2.3
-=================================
-
-Ming Zhang
-  o Improve INQUIRY, REQUEST_SENSE, and MODE_SENSE command supports
-  o Add fake RESERVE* and RELEASE* command supports
-
-
-Summary of changes from v0.2.1 to v0.2.2
-=================================
-
-FUJITA Tomonori
-  o Improve the write performance of the file IO mode
-
-Ming Zhang
-  o Fix unupdated pg_cnt when allocating a new tcmnd
-  o Several cleanups
-
-
-Summary of changes from v0.2.0 to v0.2.1
-=================================
-
-FUJITA Tomonori
-  o Fix a bug that makes the target use CPU unnecessarily
-  o Add a feature that enable you to pass options to an IO mode
-
-
-Summary of changes from v0.1.0 to v0.2.0
-=================================
-
-FUJITA Tomonori
-  o Rewrite read and write kernel threads which perform network IO
-  o Fix race issues in the proc interface
-  o Fix shutdown code
-
-Ming Zhang
-  o Fix memory leaks in file and block IO modes
-
-
-Summary of changes from the ardis v20040211 to v0.1.0
-=================================
-
-FUJITA Tomonori
-  o Remove a kernel patch. Multiple threads execute I/O operations
-  o Replace IO functions with the kernel standard functions
-  o Add multiple IO modes feature
-  o Fix several race issues
-  o Fix several out-of-memory situation bugs
index f6fae45..7e47cca 100644 (file)
@@ -1,7 +1,7 @@
 iSCSI SCST target driver
 ========================
 
-Version 1.0.2, XX XXXXX 2009
+Version 2.0.0, XX XXXXX 2010
 ----------------------------
 
 ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET)
@@ -14,7 +14,7 @@ ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET)
 
 See more info at http://scst.sourceforge.net/target_iscsi.html.
 
-This version is compatible with SCST version 1.0.2 and higher.
+This version is compatible with SCST version 2.0.0 and higher.
 
 
 Installation if your Linux kernel already has iSCSI-SCST built-in
@@ -26,8 +26,7 @@ Simply run "make all", then "make install".
 Installation out of Linux kernel tree
 -------------------------------------
 
-Basically as in README-IET, where file names are changed as specified
-above.
+See HOWTOs in the doc/ subdirectory.
 
 Only vanilla kernels from kernel.org and RHEL/CentOS 5.2 kernels are
 supported, but it should work on other (vendors') kernels, if you manage
@@ -52,20 +51,18 @@ recent kernel messages).
 To use full power of TCP zero-copy transmit functions, especially
 dealing with user space supplied via scst_user module memory, iSCSI-SCST
 needs to be notified when Linux networking finished data transmission.
-Patch put_page_callback-<kernel-version>.patch provides such
-functionality. The corresponding version of it should be applied on your
-kernel. Then you should enable CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
-kernel config option. This is highly recommended, but not required. Basically,
-you should consider using of this patch as some optimization, which IET
-doesn't have, so if you don't use it, you will just revert to the
-original IET behavior, when for data transmission:
+For that you should enable CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
+kernel config option. This is highly recommended, but not required.
+Basically, iSCSI-SCST works fine with an unpatched Linux kernel with the
+same or better speed as other open source iSCSI targets, including IET,
+but if you want even better performance you have to patch and rebuild
+the kernel. Without CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
+enabled you will just revert to the original behavior of other open
+source iSCSI targets, when for data transmission:
 
  - For in-kernel allocated memory (scst_vdisk and pass-through
    handlers) usage of SGV cache on transmit path (READ-type commands)
    will be disabled, but data will still be sent in zero-copy manner.
-   The performance hit will be not big, but performance will still
-   remain better, than for IET, because SGV cache will remain used on
-   receive path while IET doesn't have such feature.
 
  - For user space allocated memory (scst_user handler) all transmitted
    data will be additionally copied into temporary TCP buffers. The
@@ -109,24 +106,16 @@ or
 Usage
 -----
 
-See in doc/iscsi-scst-howto.txt examples how to configure iSCSI-SCST.
+See HOWTOs in the doc/ subdirectory.
 
-ISCSI parameters like iSNS, CHAP and target parameters are configured in
-iscsi-scstd.conf. All LUN information is configured using the
-corresponding SCST interface. See in SCST README file section "Access
-and devices visibility management (LUN masking)" to find out how to do
-it. It is highly recommended to use scstadmin utility for that purpose.
-The LUN information in iscsi-scstd.conf will be ignored. This is because
-now responsibilities are divided between the target driver (iSCSI-SCST)
-and the SCST core as it logically should be: the target driver is
-responsible for handling targets and their parameters, SCST core is
-responsible for handling backstorage.
+In 2.0.0 usage of iscsi-scstd.conf as well as iscsi-scst-adm is
+obsolete. Use the sysfs interface facilities instead.
 
 It is recommended to use TEST UNIT READY ("tur") command to check if
 iSCSI-SCST target is alive.
 
-IMPORTANT: All LUN information (access control) MUST be configured
-=========  BEFORE iscsi-scstd started!
+IMPORTANT: In the procfs build all LUN information (access control)
+=========  MUST be configured BEFORE iscsi-scstd started!
 
 Also see SCST README file how to tune for the best performance.
 
@@ -134,6 +123,405 @@ CAUTION: Working of target and initiator on the same host isn't fully
 =======  supported. See SCST README file for details.
 
 
+Sysfs interface
+---------------
+
+Starting from 2.0.0 iSCSI-SCST has sysfs interface. You can switch to it
+by running "make disable_proc". To switch back to the procfs interface
+you should run "make enable_proc". The procfs interface from version
+2.0.0 is obsolete and will be removed in one of the next versions.
+
+Root of SCST sysfs interface is /sys/kernel/scst_tgt. Root of iSCSI-SCST
+is /sys/kernel/scst_tgt/targets/iscsi. It has the following entries:
+
+ - None, one or more subdirectories for targets with name equal to names
+   of the corresponding targets.
+ - IncomingUser[num] - optional one or more attributes containing user
+   name and password for incoming discovery user name. Not exist by
+   default and can be added through "mgmt" entry, see below.
+
+ - OutgoingUser - optional attribute containing user name and password
+   for outgoing discovery user name. Not exist by default and can be
+   added through "mgmt" entry, see below.
+
+ - iSNSServer - contains name or IP address of iSNS server with optional
+   "AccessControl" attribute, which allows to enable iSNS access
+   control. Empty by default.
+
+ - enabled - using this attribute you can enable or disable iSCSI-SCST
+   accept new connections. It allows to finish configuring global
+   iSCSI-SCST attributes before it starts accepting new connections. 0
+   by default.
+
+ - open_state - read-only attribute, which allows to see if the user
+   space part of iSCSI-SCST connected to the kernel part.
+
+ - trace_level - allows to enable and disable various tracing
+   facilities. See content of this file for help how to use it.
+
+ - version - read-only attribute, which allows to see version of
+   iSCSI-SCST and enabled optional features.
+
+ - mgmt - main management entry, which allows to configure iSCSI-SCST.
+   Namely, add/delete targets as well as add/delete optional global and
+   per-target attributes. See content of this file for help how to use
+   it.
+
+Each iSCSI-SCST attribute can contain in the last line mark "[key]". It
+is automatically added mark used to allow scstadmin to see which
+attributes it should save in the config file. You can ignore it.
+
+Each target subdirectory contains the following entries:
+
+ - ini_group - subdirectory defining initiator groups for this target,
+   used to define per-initiator access control. See SCST core README for
+   more details.
+ - luns - subdirectory defining LUNs of this target. See SCST core
+   README for more details.
+ - sessions - subdirectory containing connected to this target sessions.
+ - IncomingUser[num] - optional one or more attributes containing user
+   name and password for incoming user name. Not exist by default and can
+   be added through the "mgmt" entry, see above.
+
+ - OutgoingUser - optional attribute containing user name and password
+   for outgoing user name. Not exist by default and can be added through
+   the "mgmt" entry, see above.
+
+ - Entries defining default iSCSI parameters values used during iSCSI
+   parameters negotiation.
+
+ - QueuedCommands - defines maximum number of commands queued to any
+   session of this target.
+
+ - enabled - using this attribute you can enable or disable iSCSI-SCST
+   accept new connections to this target. It allows to finish
+   configuring it before it starts accepting new connections. 0 by
+   default.
+
+ - tid - TID of this target.
+
+Subdirectory "sessions" contains one subdirectory for each connected
+session with name equal to name of the connected initiator.
+
+Each session subdirectory contains the following entries:
+
+ - One subdirectory for each TCP connection in this session. ISCSI-SCST
+   supports 1 connection per session, but the session subdirectory can
+   contain several connections: one active and other being closed.
+   
+ - Entries defining negotiated iSCSI parameters.
+
+ - initiator_name - contains initiator name
+ - sid - contains SID of this session
+ - reinstating - contains reinstatement state of this session
+ - force_close - write-only attribute, which allows to force close this
+   session. This is the only writable session attribute.
+   
+ - active_commands - contains number of active, i.e. not yet or being
+   executed, SCSI commands in this session.
+   
+ - commands - contains overall number of SCSI commands in this session.
+
+Each connection subdirectory contains the following entries:
+
+ - cid - contains CID of this connection.
+ - ip - contains IP address of the connected initiator.
+ - state - contains processing state of this connection.
+Below is a sample script, which configures 1 virtual disk "disk1" using
+/disk1 image and one target iqn.2006-10.net.vlnb:tgt with all default
+parameters:
+
+#!/bin/bash
+
+modprobe scst
+modprobe scst_vdisk
+
+echo "open disk1 /disk1 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+
+service iscsi-scst start
+
+echo "add_target iqn.2006-10.net.vlnb:tgt" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+
+echo "add disk1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
+
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled
+
+
+Below is more advanced sample script, which configures more virtual
+devices of various types, including virtual CDROM and 2 targets, one
+with all default parameters, another one with some not default
+parameters, incoming and outgoing user names for CHAP authentification,
+and special permissions for initiator iqn.2005-03.org.open-iscsi:cacdcd2520,
+which will see another set of devices. Also this sample configures CHAP
+authentication for discovery sessions and iSNS server with access control.
+
+#!/bin/bash
+
+modprobe scst
+modprobe scst_vdisk
+
+echo "open disk1 /disk1 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "open disk2 /disk2 4096 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "open blockio /dev/sda5" >/sys/kernel/scst_tgt/handlers/vdisk_blockio/mgmt
+echo "open nullio none" >/sys/kernel/scst_tgt/handlers/vdisk_nullio/mgmt
+echo "open cdrom" >/sys/kernel/scst_tgt/handlers/vcdrom/mgmt
+
+service iscsi-scst start
+
+echo "192.168.1.16 AccessControl" >/sys/kernel/scst_tgt/targets/iscsi/iSNSServer
+echo "add_attribute IncomingUser joeD 12charsecret" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+echo "add_attribute OutgoingUser jackD 12charsecret1" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+
+echo "add_target iqn.2006-10.net.vlnb:tgt" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+
+echo "add disk1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
+echo "add cdrom 1" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
+
+echo "add_target iqn.2006-10.net.vlnb:tgt1 \
+       IncomingUser1 joe2 12charsecret2; \
+       IncomingUser joe 12charsecret; \
+       OutgoingUser jim1 12charpasswd; \
+       InitialR2T                      No; \
+       ImmediateData                   Yes; \
+       MaxRecvDataSegmentLength        8192; \
+       MaxXmitDataSegmentLength        8192; \
+       MaxBurstLength                  131072; \
+       FirstBurstLength                32768; \
+       MaxOutstandingR2T               1; \
+       HeaderDigest                    CRC32C,None; \
+       DataDigest                      CRC32C,None; \
+       QueuedCommands                  8; \
+       " >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+
+echo "add disk2 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/mgmt
+echo "add nullio 26" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/mgmt
+
+echo "create special_ini" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/mgmt
+echo "add blockio 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/mgmt
+echo "add iqn.2005-03.org.open-iscsi:cacdcd2520" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/initiators/mgmt
+
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/enabled
+
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled
+
+The resulting overall SCST sysfs hierarchy with an initiator connected to
+both iSCSI-SCST targets will look like:
+
+/sys/kernel/scst_tgt
+|-- devices
+|   |-- blockio
+|   |   |-- block_size
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/0
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vdisk_blockio
+|   |   |-- read_only
+|   |   |-- removable
+|   |   |-- resync_size
+|   |   |-- size
+|   |   |-- t10_dev_id
+|   |   `-- type
+|   |-- cdrom
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/1
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vcdrom
+|   |   |-- removable
+|   |   |-- size
+|   |   |-- t10_dev_id
+|   |   `-- type
+|   |-- disk1
+|   |   |-- block_size
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vdisk_fileio
+|   |   |-- nv_cache
+|   |   |-- o_direct
+|   |   |-- read_only
+|   |   |-- removable
+|   |   |-- resync_size
+|   |   |-- size
+|   |   |-- t10_dev_id
+|   |   |-- type
+|   |   `-- write_through
+|   |-- disk2
+|   |   |-- block_size
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/0
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vdisk_fileio
+|   |   |-- nv_cache
+|   |   |-- o_direct
+|   |   |-- read_only
+|   |   |-- removable
+|   |   |-- resync_size
+|   |   |-- size
+|   |   |-- t10_dev_id
+|   |   |-- type
+|   |   `-- write_through
+|   `-- nullio
+|       |-- block_size
+|       |-- exported
+|       |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/26
+|       |-- handler -> ../../handlers/vdisk_nullio
+|       |-- read_only
+|       |-- removable
+|       |-- size
+|       |-- t10_dev_id
+|       `-- type
+|-- handlers
+|   |-- vcdrom
+|   |   |-- mgmt
+|   |   |-- trace_level
+|   |   `-- type
+|   |-- vdisk_blockio
+|   |   |-- mgmt
+|   |   |-- trace_level
+|   |   `-- type
+|   |-- vdisk_fileio
+|   |   |-- mgmt
+|   |   |-- trace_level
+|   |   `-- type
+|   `-- vdisk_nullio
+|       |-- mgmt
+|       |-- trace_level
+|       `-- type
+|-- sgv
+|   |-- global_stats
+|   |-- sgv
+|   |   `-- stats
+|   |-- sgv-clust
+|   |   `-- stats
+|   `-- sgv-dma
+|       `-- stats
+|-- targets
+|   `-- iscsi
+|       |-- IncomingUser
+|       |-- OutgoingUser
+|       |-- enabled
+|       |-- iSNSServer
+|       |-- iqn.2006-10.net.vlnb:tgt
+|       |   |-- DataDigest
+|       |   |-- FirstBurstLength
+|       |   |-- HeaderDigest
+|       |   |-- ImmediateData
+|       |   |-- InitialR2T
+|       |   |-- MaxBurstLength
+|       |   |-- MaxOutstandingR2T
+|       |   |-- MaxRecvDataSegmentLength
+|       |   |-- MaxXmitDataSegmentLength
+|       |   |-- QueuedCommands
+|       |   |-- enabled
+|       |   |-- ini_group
+|       |   |   `-- mgmt
+|       |   |-- luns
+|       |   |   |-- 0
+|       |   |   |   |-- device -> ../../../../../devices/disk1
+|       |   |   |   `-- read_only
+|       |   |   |-- 1
+|       |   |   |   |-- device -> ../../../../../devices/cdrom
+|       |   |   |   `-- read_only
+|       |   |   `-- mgmt
+|       |   |-- sessions
+|       |   |   `-- iqn.2005-03.org.open-iscsi:cacdcd2520
+|       |   |       |-- 10.170.75.2
+|       |   |       |   |-- cid
+|       |   |       |   |-- ip
+|       |   |       |   `-- state
+|       |   |       |-- DataDigest
+|       |   |       |-- FirstBurstLength
+|       |   |       |-- HeaderDigest
+|       |   |       |-- ImmediateData
+|       |   |       |-- InitialR2T
+|       |   |       |-- MaxBurstLength
+|       |   |       |-- MaxOutstandingR2T
+|       |   |       |-- MaxRecvDataSegmentLength
+|       |   |       |-- MaxXmitDataSegmentLength
+|       |   |       |-- active_commands
+|       |   |       |-- commands
+|       |   |       |-- force_close
+|       |   |       |-- initiator_name
+|       |   |       |-- reinstating
+|       |   |       `-- sid
+|       |   `-- tid
+|       |-- iqn.2006-10.net.vlnb:tgt1
+|       |   |-- DataDigest
+|       |   |-- FirstBurstLength
+|       |   |-- HeaderDigest
+|       |   |-- ImmediateData
+|       |   |-- IncomingUser
+|       |   |-- IncomingUser1
+|       |   |-- InitialR2T
+|       |   |-- MaxBurstLength
+|       |   |-- MaxOutstandingR2T
+|       |   |-- MaxRecvDataSegmentLength
+|       |   |-- MaxXmitDataSegmentLength
+|       |   |-- OutgoingUser
+|       |   |-- QueuedCommands
+|       |   |-- enabled
+|       |   |-- ini_group
+|       |   |   |-- mgmt
+|       |   |   `-- special_ini
+|       |   |       |-- initiators
+|       |   |       |   |-- iqn.2005-03.org.open-iscsi:cacdcd2520
+|       |   |       |   `-- mgmt
+|       |   |       `-- luns
+|       |   |           |-- 0
+|       |   |           |   |-- device -> ../../../../../../../devices/blockio
+|       |   |           |   `-- read_only
+|       |   |           `-- mgmt
+|       |   |-- luns
+|       |   |   |-- 0
+|       |   |   |   |-- device -> ../../../../../devices/disk2
+|       |   |   |   `-- read_only
+|       |   |   |-- 26
+|       |   |   |   |-- device -> ../../../../../devices/nullio
+|       |   |   |   `-- read_only
+|       |   |   `-- mgmt
+|       |   |-- sessions
+|       |   |   `-- iqn.2005-03.org.open-iscsi:cacdcd2520
+|       |   |       |-- 10.170.75.2
+|       |   |       |   |-- cid
+|       |   |       |   |-- ip
+|       |   |       |   `-- state
+|       |   |       |-- DataDigest
+|       |   |       |-- FirstBurstLength
+|       |   |       |-- HeaderDigest
+|       |   |       |-- ImmediateData
+|       |   |       |-- InitialR2T
+|       |   |       |-- MaxBurstLength
+|       |   |       |-- MaxOutstandingR2T
+|       |   |       |-- MaxRecvDataSegmentLength
+|       |   |       |-- MaxXmitDataSegmentLength
+|       |   |       |-- active_commands
+|       |   |       |-- commands
+|       |   |       |-- force_close
+|       |   |       |-- initiator_name
+|       |   |       |-- reinstating
+|       |   |       `-- sid
+|       |   `-- tid
+|       |-- mgmt
+|       |-- open_state
+|       |-- trace_level
+|       `-- version
+|-- threads
+|-- trace_level
+`-- version
+
+
 Troubleshooting
 ---------------
 
@@ -160,9 +548,8 @@ abort or reset messages. It can happen under high I/O load, when your
 target's backstorage gets overloaded, or working over a slow link, when
 the link can't serve all the queued commands on time, 
 
-To workaround it you can reduce QueuedCommands parameter in
-iscsi-scstd.conf file for the corresponding target to some lower value,
-like 8 (default is 32).
+To workaround it you can reduce QueuedCommands parameter for the
+corresponding target to some lower value, like 8 (default is 32).
 
 Also see SCST README file for more details about that issue and ways to
 prevent it.
@@ -229,8 +616,6 @@ Credits
 
 Thanks to:
 
- * IET developers for IET
-
  * Ming Zhang <blackmagic02881@gmail.com> for fixes
 
  * Krzysztof Blaszkowski <kb@sysmikro.com.pl> for many fixes
diff --git a/iscsi-scst/README-IET b/iscsi-scst/README-IET
deleted file mode 100644 (file)
index 83829c9..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-Introduction
--------------
-iSCSI Enterprise Target is for building an iSCSI storage system on
-Linux. It is aimed at developing an iSCSI target satisfying enterprise
-requirements.
-
-We borrow code from an Ardis iSCSI target (with respect to the GPL).
-
-
-Installation
--------------
-The iSCSI target requires a host running the Linux operating system
-with a kernel version of 2.6.19 (2.6.14 - 2.6.18 kernels using
-backward compatibility patches, see below) or newer. You need to
-enable "Cryptographic API" under "Cryptographic options" in the kernel
-config.  You also need to enable "CRC32c CRC algorithm" if you use
-header or data digests. They are the kernel options, CONFIG_CRYPTO and
-CONFIG_CRYPTO_CRC32C, respectively. The user-space code requires
-OpenSSL library (http://www.openssl.org/).
-
-The iSCSI target consists of kernel modules and a daemon. Since IET
-is generally targeted at the latest stable mainline kernel, you might
-need to apply a backward compatibility patch to compile it against
-older kernel versions:
-
-       patch -p0 < patches/compat-2.6.14-2.6.18.patch
-
-Compilation of the kernel modules require the path to the kernel
-sources:
-
-       make KSRC=<kernel-src>
-
-The path can also be set by editing the main Makefile. If KSRC is
-omitted, make program will try to locate the kernel sources for
-current running kernel.  Be sure to check whether it finds the right
-kernel sources.
-
-This will build the modules, the daemon, and the control tool. To
-install both, use:
-
-       make KSRC=<kernel-src> install
-
-The kernel modules will be install in the module directory of the
-kernel. The daemon and the control tool will be installed as ietd and
-ietadm under /usr/sbin. The boot script will be installed as
-iscsi-targt under /etc/init.d.
-
-If you use Linux distribution that does not have /etc/init.d, the
-boot script will not be installed. So you need to install it to an
-appropriate directory manually.
-
-
-Configuration
--------------
-The daemon is configured via the configuration file /etc/ietd.conf.
-See the man page and the example file for the current syntax.
-
-The ietadm utility is for managing IET software dynamically. You can
-change the configurations of running targets. See the help message.
-
-The access control based on initiator address and target name patterns
-is configured via two configuration files (/etc/initiators.allow and
-/etc/initiators.deny). These files work like tcpd files
-(/etc/hosts.allow and /etc/hosts.deny). This feature enables you to
-hide a particular targets from some initiators. See the example files
-for the supported expressions. You can change the configuration
-dynamically. The modifications to the files become effective
-immediately.
-
-
-Starting
--------------
-The target is not started automatically. So execute:
-
-       /etc/init.d/iscsi-target start
-
-Note that you must edit the configuration file before starting the
-target.
-
-
-Stopping
--------------
-Execute:
-
-       /etc/init.d/iscsi-target stop
-
-Contact
--------------
-Please send bug reports, comments, feature requests etc. to our
-mailing list <iscsitarget-devel@lists.sourceforge.net>.
-
-
-Developer Notes
-----------------
-The central resource for IET development is the
-<iscsitarget-devel@lists.sourceforge.net> mailing list.
-
-Our subversion repository can be found at: svn://svn.berlios.de/iscsitarget
-
-When submitting patches, please diff against the code in our repository's
-trunk and adhere otherwise to the same rules that apply to Linux kernel
-development, in particular the Linux kernel coding style
-($KSRC/Documentation/CodingStyle) and the rules for submitting patches
-($KSRC/Documentation/SubmittingPatches), i.e. please send patches inline as
-plain text.
index 2b20209..e3e4d1c 100644 (file)
@@ -1,54 +1,15 @@
 iSCSI SCST target driver
 ========================
 
-Version 1.0.2, XX XXXXX 2009
-----------------------------
-
-This driver is a forked with all respects version of iSCSI Enterprise
-Target (IET) (http://iscsitarget.sourceforge.net/) with updates to work
-over SCST as well as with many improvements and bugfixes (see ChangeLog
-file). The reason of fork is that the necessary changes are intrusive
-and with the current IET merge policy, where only simple bugfix-like
-patches, which doesn't touch the core code, could be merged, it is very
-unlikely that they will be merged in the main IET trunk.
-
-To let it be installed and work at the same host together with IET
-simultaneously all the driver's modules and files were renamed:
-
- * ietd.conf -> iscsi-scstd.conf
- * ietadm -> iscsi-scst-adm
- * ietd -> iscsi-scstd
- * iscsi-target -> iscsi-scst
- * iscsi-target.ko -> iscsi-scst.ko
-
-To use full power of TCP zero-copy transmit functions, especially
-dealing with user space supplied via scst_user module memory, iSCSI-SCST
-needs to be notified when Linux networking finished data transmission.
-For that you should enable CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
-kernel config option. This is highly recommended, but not required. Basically,
-you should consider usage of this option as some optimization, which IET
-doesn't have, so if you don't use it, you will just revert to the
-original IET behavior, when for data transmission:
-
- - For in-kernel allocated memory (scst_vdisk and pass-through
-   handlers) usage of SGV cache on transmit path (READ-type commands)
-   will be disabled, but data will still be sent in zero-copy manner.
-   The performance hit will be not big, but performance will still
-   remain better, than for IET, because SGV cache will remain used on
-   receive path while IET doesn't have such feature.
-
- - For user space allocated memory (scst_user handler) all transmitted
-   data will be additionally copied into temporary TCP buffers. The
-   performance hit will be quite noticeable.
-
-Note, that if your network hardware does not support TX offload
-functions or has them disabled, then TCP zero-copy transmit functions on
-your system will not be used by Linux networking in any case, so
-put_page_callback patch will not be able to improve performance for you.
-You can check your network hardware offload capabilities by command
-"ethtool -k ethX", where X is the network device number. At least
-"tx-checksumming" and "scatter-gather" should be enabled.
+ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET)
+(http://iscsitarget.sourceforge.net). Reasons of the fork were:
 
+ - To be able to use full power of SCST core.
+
+ - To fix all the problems, corner cases issues and iSCSI standard
+   violations which IET has.
+
+See more info at http://scst.sourceforge.net/target_iscsi.html.
 
 Usage
 -----
@@ -56,22 +17,14 @@ Usage
 See in http://scst.sourceforge.net/iscsi-scst-howto.txt how to configure
 iSCSI-SCST.
 
-ISCSI parameters like iSNS, CHAP and target parameters are configured in
-iscsi-scstd.conf. All LUN information is configured using the
-corresponding SCST interface. See in SCST README file section "Access
-and devices visibility management (LUN masking)" to find out how to do
-it. It is highly recommended to use scstadmin utility for that purpose.
-The LUN information in iscsi-scstd.conf will be ignored. This is because
-now responsibilities are divided between the target driver (iSCSI-SCST)
-and the SCST core as it logically should be: the target driver is
-responsible for handling targets and their parameters, SCST core is
-responsible for handling backstorage.
+In 2.0.0 usage of iscsi-scstd.conf as well as iscsi-scst-adm is
+obsolete. Use the sysfs interface facilities instead.
 
 It is recommended to use TEST UNIT READY ("tur") command to check if
 iSCSI-SCST target is alive.
 
-IMPORTANT: All LUN information (access control) MUST be configured
-=========  BEFORE iscsi-scstd started!
+IMPORTANT: In the procfs build all LUN information (access control)
+=========  MUST be configured BEFORE iscsi-scstd started!
 
 Also see SCST README file how to tune for the best performance.
 
@@ -79,6 +32,405 @@ CAUTION: Working of target and initiator on the same host isn't fully
 =======  supported. See SCST README file for details.
 
 
+Sysfs interface
+---------------
+
+Starting from 2.0.0 iSCSI-SCST has sysfs interface. You can switch to it
+by running "make disable_proc". To switch back to the procfs interface
+you should run "make enable_proc". The procfs interface from version
+2.0.0 is obsolete and will be removed in one of the next versions.
+
+Root of SCST sysfs interface is /sys/kernel/scst_tgt. Root of iSCSI-SCST
+is /sys/kernel/scst_tgt/targets/iscsi. It has the following entries:
+
+ - None, one or more subdirectories for targets with name equal to names
+   of the corresponding targets.
+ - IncomingUser[num] - optional one or more attributes containing user
+   name and password for incoming discovery user name. Not exist by
+   default and can be added through "mgmt" entry, see below.
+
+ - OutgoingUser - optional attribute containing user name and password
+   for outgoing discovery user name. Not exist by default and can be
+   added through "mgmt" entry, see below.
+
+ - iSNSServer - contains name or IP address of iSNS server with optional
+   "AccessControl" attribute, which allows to enable iSNS access
+   control. Empty by default.
+
+ - enabled - using this attribute you can enable or disable iSCSI-SCST
+   accept new connections. It allows to finish configuring global
+   iSCSI-SCST attributes before it starts accepting new connections. 0
+   by default.
+
+ - open_state - read-only attribute, which allows to see if the user
+   space part of iSCSI-SCST connected to the kernel part.
+
+ - trace_level - allows to enable and disable various tracing
+   facilities. See content of this file for help how to use it.
+
+ - version - read-only attribute, which allows to see version of
+   iSCSI-SCST and enabled optional features.
+
+ - mgmt - main management entry, which allows to configure iSCSI-SCST.
+   Namely, add/delete targets as well as add/delete optional global and
+   per-target attributes. See content of this file for help how to use
+   it.
+
+Each iSCSI-SCST attribute can contain in the last line mark "[key]". It
+is automatically added mark used to allow scstadmin to see which
+attributes it should save in the config file. You can ignore it.
+
+Each target subdirectory contains the following entries:
+
+ - ini_group - subdirectory defining initiator groups for this target,
+   used to define per-initiator access control. See SCST core README for
+   more details.
+ - luns - subdirectory defining LUNs of this target. See SCST core
+   README for more details.
+ - sessions - subdirectory containing connected to this target sessions.
+ - IncomingUser[num] - optional one or more attributes containing user
+   name and password for incoming user name. Not exist by default and can
+   be added through the "mgmt" entry, see above.
+
+ - OutgoingUser - optional attribute containing user name and password
+   for outgoing user name. Not exist by default and can be added through
+   the "mgmt" entry, see above.
+
+ - Entries defining default iSCSI parameters values used during iSCSI
+   parameters negotiation.
+
+ - QueuedCommands - defines maximum number of commands queued to any
+   session of this target.
+
+ - enabled - using this attribute you can enable or disable iSCSI-SCST
+   accept new connections to this target. It allows to finish
+   configuring it before it starts accepting new connections. 0 by
+   default.
+
+ - tid - TID of this target.
+
+Subdirectory "sessions" contains one subdirectory for each connected
+session with name equal to name of the connected initiator.
+
+Each session subdirectory contains the following entries:
+
+ - One subdirectory for each TCP connection in this session. ISCSI-SCST
+   supports 1 connection per session, but the session subdirectory can
+   contain several connections: one active and other being closed.
+   
+ - Entries defining negotiated iSCSI parameters.
+
+ - initiator_name - contains initiator name
+ - sid - contains SID of this session
+ - reinstating - contains reinstatement state of this session
+ - force_close - write-only attribute, which allows to force close this
+   session. This is the only writable session attribute.
+   
+ - active_commands - contains number of active, i.e. not yet or being
+   executed, SCSI commands in this session.
+   
+ - commands - contains overall number of SCSI commands in this session.
+
+Each connection subdirectory contains the following entries:
+
+ - cid - contains CID of this connection.
+ - ip - contains IP address of the connected initiator.
+ - state - contains processing state of this connection.
+Below is a sample script, which configures 1 virtual disk "disk1" using
+/disk1 image and one target iqn.2006-10.net.vlnb:tgt with all default
+parameters:
+
+#!/bin/bash
+
+modprobe scst
+modprobe scst_vdisk
+
+echo "open disk1 /disk1 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+
+service iscsi-scst start
+
+echo "add_target iqn.2006-10.net.vlnb:tgt" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+
+echo "add disk1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
+
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled
+
+
+Below is more advanced sample script, which configures more virtual
+devices of various types, including virtual CDROM and 2 targets, one
+with all default parameters, another one with some not default
+parameters, incoming and outgoing user names for CHAP authentification,
+and special permissions for initiator iqn.2005-03.org.open-iscsi:cacdcd2520,
+which will see another set of devices. Also this sample configures CHAP
+authentication for discovery sessions and iSNS server with access control.
+
+#!/bin/bash
+
+modprobe scst
+modprobe scst_vdisk
+
+echo "open disk1 /disk1 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "open disk2 /disk2 4096 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "open blockio /dev/sda5" >/sys/kernel/scst_tgt/handlers/vdisk_blockio/mgmt
+echo "open nullio none" >/sys/kernel/scst_tgt/handlers/vdisk_nullio/mgmt
+echo "open cdrom" >/sys/kernel/scst_tgt/handlers/vcdrom/mgmt
+
+service iscsi-scst start
+
+echo "192.168.1.16 AccessControl" >/sys/kernel/scst_tgt/targets/iscsi/iSNSServer
+echo "add_attribute IncomingUser joeD 12charsecret" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+echo "add_attribute OutgoingUser jackD 12charsecret1" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+
+echo "add_target iqn.2006-10.net.vlnb:tgt" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+
+echo "add disk1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
+echo "add cdrom 1" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
+
+echo "add_target iqn.2006-10.net.vlnb:tgt1 \
+       IncomingUser1 joe2 12charsecret2; \
+       IncomingUser joe 12charsecret; \
+       OutgoingUser jim1 12charpasswd; \
+       InitialR2T                      No; \
+       ImmediateData                   Yes; \
+       MaxRecvDataSegmentLength        8192; \
+       MaxXmitDataSegmentLength        8192; \
+       MaxBurstLength                  131072; \
+       FirstBurstLength                32768; \
+       MaxOutstandingR2T               1; \
+       HeaderDigest                    CRC32C,None; \
+       DataDigest                      CRC32C,None; \
+       QueuedCommands                  8; \
+       " >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+
+echo "add disk2 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/mgmt
+echo "add nullio 26" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/mgmt
+
+echo "create special_ini" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/mgmt
+echo "add blockio 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/mgmt
+echo "add iqn.2005-03.org.open-iscsi:cacdcd2520" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/initiators/mgmt
+
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/enabled
+
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled
+
+The resulting overall SCST sysfs hierarchy with an initiator connected to
+both iSCSI-SCST targets will look like:
+
+/sys/kernel/scst_tgt
+|-- devices
+|   |-- blockio
+|   |   |-- block_size
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/0
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vdisk_blockio
+|   |   |-- read_only
+|   |   |-- removable
+|   |   |-- resync_size
+|   |   |-- size
+|   |   |-- t10_dev_id
+|   |   `-- type
+|   |-- cdrom
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/1
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vcdrom
+|   |   |-- removable
+|   |   |-- size
+|   |   |-- t10_dev_id
+|   |   `-- type
+|   |-- disk1
+|   |   |-- block_size
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vdisk_fileio
+|   |   |-- nv_cache
+|   |   |-- o_direct
+|   |   |-- read_only
+|   |   |-- removable
+|   |   |-- resync_size
+|   |   |-- size
+|   |   |-- t10_dev_id
+|   |   |-- type
+|   |   `-- write_through
+|   |-- disk2
+|   |   |-- block_size
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/0
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vdisk_fileio
+|   |   |-- nv_cache
+|   |   |-- o_direct
+|   |   |-- read_only
+|   |   |-- removable
+|   |   |-- resync_size
+|   |   |-- size
+|   |   |-- t10_dev_id
+|   |   |-- type
+|   |   `-- write_through
+|   `-- nullio
+|       |-- block_size
+|       |-- exported
+|       |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/26
+|       |-- handler -> ../../handlers/vdisk_nullio
+|       |-- read_only
+|       |-- removable
+|       |-- size
+|       |-- t10_dev_id
+|       `-- type
+|-- handlers
+|   |-- vcdrom
+|   |   |-- mgmt
+|   |   |-- trace_level
+|   |   `-- type
+|   |-- vdisk_blockio
+|   |   |-- mgmt
+|   |   |-- trace_level
+|   |   `-- type
+|   |-- vdisk_fileio
+|   |   |-- mgmt
+|   |   |-- trace_level
+|   |   `-- type
+|   `-- vdisk_nullio
+|       |-- mgmt
+|       |-- trace_level
+|       `-- type
+|-- sgv
+|   |-- global_stats
+|   |-- sgv
+|   |   `-- stats
+|   |-- sgv-clust
+|   |   `-- stats
+|   `-- sgv-dma
+|       `-- stats
+|-- targets
+|   `-- iscsi
+|       |-- IncomingUser
+|       |-- OutgoingUser
+|       |-- enabled
+|       |-- iSNSServer
+|       |-- iqn.2006-10.net.vlnb:tgt
+|       |   |-- DataDigest
+|       |   |-- FirstBurstLength
+|       |   |-- HeaderDigest
+|       |   |-- ImmediateData
+|       |   |-- InitialR2T
+|       |   |-- MaxBurstLength
+|       |   |-- MaxOutstandingR2T
+|       |   |-- MaxRecvDataSegmentLength
+|       |   |-- MaxXmitDataSegmentLength
+|       |   |-- QueuedCommands
+|       |   |-- enabled
+|       |   |-- ini_group
+|       |   |   `-- mgmt
+|       |   |-- luns
+|       |   |   |-- 0
+|       |   |   |   |-- device -> ../../../../../devices/disk1
+|       |   |   |   `-- read_only
+|       |   |   |-- 1
+|       |   |   |   |-- device -> ../../../../../devices/cdrom
+|       |   |   |   `-- read_only
+|       |   |   `-- mgmt
+|       |   |-- sessions
+|       |   |   `-- iqn.2005-03.org.open-iscsi:cacdcd2520
+|       |   |       |-- 10.170.75.2
+|       |   |       |   |-- cid
+|       |   |       |   |-- ip
+|       |   |       |   `-- state
+|       |   |       |-- DataDigest
+|       |   |       |-- FirstBurstLength
+|       |   |       |-- HeaderDigest
+|       |   |       |-- ImmediateData
+|       |   |       |-- InitialR2T
+|       |   |       |-- MaxBurstLength
+|       |   |       |-- MaxOutstandingR2T
+|       |   |       |-- MaxRecvDataSegmentLength
+|       |   |       |-- MaxXmitDataSegmentLength
+|       |   |       |-- active_commands
+|       |   |       |-- commands
+|       |   |       |-- force_close
+|       |   |       |-- initiator_name
+|       |   |       |-- reinstating
+|       |   |       `-- sid
+|       |   `-- tid
+|       |-- iqn.2006-10.net.vlnb:tgt1
+|       |   |-- DataDigest
+|       |   |-- FirstBurstLength
+|       |   |-- HeaderDigest
+|       |   |-- ImmediateData
+|       |   |-- IncomingUser
+|       |   |-- IncomingUser1
+|       |   |-- InitialR2T
+|       |   |-- MaxBurstLength
+|       |   |-- MaxOutstandingR2T
+|       |   |-- MaxRecvDataSegmentLength
+|       |   |-- MaxXmitDataSegmentLength
+|       |   |-- OutgoingUser
+|       |   |-- QueuedCommands
+|       |   |-- enabled
+|       |   |-- ini_group
+|       |   |   |-- mgmt
+|       |   |   `-- special_ini
+|       |   |       |-- initiators
+|       |   |       |   |-- iqn.2005-03.org.open-iscsi:cacdcd2520
+|       |   |       |   `-- mgmt
+|       |   |       `-- luns
+|       |   |           |-- 0
+|       |   |           |   |-- device -> ../../../../../../../devices/blockio
+|       |   |           |   `-- read_only
+|       |   |           `-- mgmt
+|       |   |-- luns
+|       |   |   |-- 0
+|       |   |   |   |-- device -> ../../../../../devices/disk2
+|       |   |   |   `-- read_only
+|       |   |   |-- 26
+|       |   |   |   |-- device -> ../../../../../devices/nullio
+|       |   |   |   `-- read_only
+|       |   |   `-- mgmt
+|       |   |-- sessions
+|       |   |   `-- iqn.2005-03.org.open-iscsi:cacdcd2520
+|       |   |       |-- 10.170.75.2
+|       |   |       |   |-- cid
+|       |   |       |   |-- ip
+|       |   |       |   `-- state
+|       |   |       |-- DataDigest
+|       |   |       |-- FirstBurstLength
+|       |   |       |-- HeaderDigest
+|       |   |       |-- ImmediateData
+|       |   |       |-- InitialR2T
+|       |   |       |-- MaxBurstLength
+|       |   |       |-- MaxOutstandingR2T
+|       |   |       |-- MaxRecvDataSegmentLength
+|       |   |       |-- MaxXmitDataSegmentLength
+|       |   |       |-- active_commands
+|       |   |       |-- commands
+|       |   |       |-- force_close
+|       |   |       |-- initiator_name
+|       |   |       |-- reinstating
+|       |   |       `-- sid
+|       |   `-- tid
+|       |-- mgmt
+|       |-- open_state
+|       |-- trace_level
+|       `-- version
+|-- threads
+|-- trace_level
+`-- version
+
+
 Troubleshooting
 ---------------
 
@@ -103,9 +455,8 @@ abort or reset messages. It can happen under high I/O load, when your
 target's backstorage gets overloaded, or working over a slow link, when
 the link can't serve all the queued commands on time,
 
-To workaround it you can reduce QueuedCommands parameter in
-iscsi-scstd.conf file for the corresponding target to some lower value,
-like 8 (default is 32).
+To workaround it you can reduce QueuedCommands parameter for the
+corresponding target to some lower value, like 8 (default is 32).
 
 Also see SCST README file for more details about that issue and ways to
 prevent it.
@@ -152,8 +503,6 @@ Credits
 
 Thanks to:
 
- * IET developers for IET
-
  * Ming Zhang <blackmagic02881@gmail.com> for fixes
 
  * Krzysztof Blaszkowski <kb@sysmikro.com.pl> for many fixes
index 5754d42..e9624d9 100644 (file)
@@ -1,3 +1,20 @@
- - Fix support of ranges in parameters negotiation.
+ - __cmnd_abort() must be called by SCST core through new callback
+   instead of the driver itself.
+
+ - Remove the "poor man solution" suspending. Instead, immediate/unsolicited
+   data for suspended commands should be received in the dummy_page and
+   then, when they are resumed, aborted with BUSY status. All other
+   commands should proceed normally.
+
+ - Separate iSCSI request and response structures. Currently they share the
+   same structure. This architecture decision derived from IET and makes
+   the code a lot less readable, maintainable and effective as well as more
+   errors prone, so it must be corrected.
+ - Code beautifying, i.e. make it be written in the same nice looking style.
+   Particularly, all functions and variables names should share the same style.
+
+ - Fix support of ranges in parameters negotiation. Are there any initiators who
+   use ranges and, hence, can be used for testing?
 
  - Minor "ToDo"'s spread in the code.
index 6aa9fdd..be008e4 100644 (file)
 #define aligned_u64 uint64_t __attribute__((aligned(8)))
 #endif
 
-struct iscsi_kern_target_info {
-       u32 tid;
-       char name[ISCSI_NAME_LEN];
-};
-
-struct iscsi_kern_session_info {
-       u32 tid;
-
-       aligned_u64 sid;
-       char initiator_name[ISCSI_NAME_LEN];
-       char user_name[ISCSI_NAME_LEN];
-       u32 exp_cmd_sn;
-};
-
-#define DIGEST_ALL             (DIGEST_NONE | DIGEST_CRC32C)
-#define DIGEST_NONE            (1 << 0)
-#define DIGEST_CRC32C           (1 << 1)
-
-struct iscsi_kern_conn_info {
-       u32 tid;
-       aligned_u64 sid;
-
-       u32 cid;
-       u32 stat_sn;
-       u32 exp_stat_sn;
-       int fd;
-};
+#define ISCSI_MAX_ATTR_NAME_LEN                50
+#define ISCSI_MAX_ATTR_VALUE_LEN       512
 
 enum {
        key_initial_r2t,
@@ -95,20 +70,74 @@ enum {
        key_target,
 };
 
-struct iscsi_kern_param_info {
+struct iscsi_kern_target_info {
+       u32 tid;
+       u32 cookie;
+       char name[ISCSI_NAME_LEN];
+       u32 attrs_num;
+       aligned_u64 attrs_ptr;
+};
+
+struct iscsi_kern_session_info {
+       u32 tid;
+       aligned_u64 sid;
+       char initiator_name[ISCSI_NAME_LEN];
+#ifdef CONFIG_SCST_PROC
+       char user_name[ISCSI_NAME_LEN];
+#endif
+       u32 exp_cmd_sn;
+       s32 session_params[session_key_last];
+       s32 target_params[target_key_last];
+};
+
+#define DIGEST_ALL             (DIGEST_NONE | DIGEST_CRC32C)
+#define DIGEST_NONE            (1 << 0)
+#define DIGEST_CRC32C           (1 << 1)
+
+struct iscsi_kern_conn_info {
        u32 tid;
        aligned_u64 sid;
 
-       u32 param_type;
+       u32 cid;
+       u32 stat_sn;
+       u32 exp_stat_sn;
+       int fd;
+};
+
+struct iscsi_kern_attr {
+       u32 mode;
+       char name[ISCSI_MAX_ATTR_NAME_LEN];
+};
+
+struct iscsi_kern_mgmt_cmd_res_info {
+       u32 tid;
+       u32 cookie;
+       u32 req_cmd;
+       u32 result;
+       char value[ISCSI_MAX_ATTR_VALUE_LEN];
+};
+
+struct iscsi_kern_params_info {
+       u32 tid;
+       aligned_u64 sid;
+
+       u32 params_type;
        u32 partial;
 
-       s32 session_param[session_key_last];
-       s32 target_param[target_key_last];
+       s32 session_params[session_key_last];
+       s32 target_params[target_key_last];
 };
 
 enum iscsi_kern_event_code {
+#ifndef CONFIG_SCST_PROC
+       E_ADD_TARGET,
+       E_DEL_TARGET,
+       E_MGMT_CMD,
        E_ENABLE_TARGET,
        E_DISABLE_TARGET,
+       E_GET_ATTR_VALUE,
+       E_SET_ATTR_VALUE,
+#endif
        E_CONN_CLOSE,
 };
 
@@ -117,10 +146,26 @@ struct iscsi_kern_event {
        aligned_u64 sid;
        u32 cid;
        u32 code;
+       u32 cookie;
+       char target_name[ISCSI_NAME_LEN];
+       u32 param1_size;
+       u32 param2_size;
 };
 
 struct iscsi_kern_register_info {
-       aligned_u64 version;
+       union {
+               aligned_u64 version;
+               struct {
+                       int max_data_seg_len;
+                       int max_queued_cmds;
+               };
+       };
+};
+
+struct iscsi_kern_attr_info {
+       u32 tid;
+       u32 cookie;
+       struct iscsi_kern_attr attr;
 };
 
 #define        DEFAULT_NR_QUEUED_CMNDS 32
@@ -129,19 +174,21 @@ struct iscsi_kern_register_info {
 
 #define NETLINK_ISCSI_SCST     25
 
-/* On success returns MAX_DATA_SEG_LEN */
-#define REGISTER_USERD         _IOW('s', 0, struct iscsi_kern_register_info)
-
+#define REGISTER_USERD         _IOWR('s', 0, struct iscsi_kern_register_info)
 #define ADD_TARGET             _IOW('s', 1, struct iscsi_kern_target_info)
 #define DEL_TARGET             _IOW('s', 2, struct iscsi_kern_target_info)
-#define ENABLE_TARGET          _IOW('s', 3, struct iscsi_kern_target_info)
-#define DISABLE_TARGET         _IOW('s', 4, struct iscsi_kern_target_info)
-#define ADD_SESSION            _IOW('s', 5, struct iscsi_kern_session_info)
-#define DEL_SESSION            _IOW('s', 6, struct iscsi_kern_session_info)
-#define ADD_CONN               _IOW('s', 7, struct iscsi_kern_conn_info)
-#define DEL_CONN               _IOW('s', 8, struct iscsi_kern_conn_info)
-#define ISCSI_PARAM_SET                _IOW('s', 9, struct iscsi_kern_param_info)
-#define ISCSI_PARAM_GET                _IOWR('s', 10, struct iscsi_kern_param_info)
+#define ADD_SESSION            _IOW('s', 3, struct iscsi_kern_session_info)
+#define DEL_SESSION            _IOW('s', 4, struct iscsi_kern_session_info)
+#define ADD_CONN               _IOW('s', 5, struct iscsi_kern_conn_info)
+#define DEL_CONN               _IOW('s', 6, struct iscsi_kern_conn_info)
+#define ISCSI_PARAM_SET                _IOW('s', 7, struct iscsi_kern_params_info)
+#define ISCSI_PARAM_GET                _IOWR('s', 8, struct iscsi_kern_params_info)
+
+#ifndef CONFIG_SCST_PROC
+#define ISCSI_ATTR_ADD         _IOW('s', 9, struct iscsi_kern_attr_info)
+#define ISCSI_ATTR_DEL         _IOW('s', 10, struct iscsi_kern_attr_info)
+#define MGMT_CMD_CALLBACK      _IOW('s', 11, struct iscsi_kern_mgmt_cmd_res_info)
+#endif
 
 static inline int iscsi_is_key_internal(int key)
 {
index fd3f8a1..7953480 100644 (file)
@@ -21,4 +21,4 @@
 #define ISCSI_VERSION_STRING_SUFFIX
 #endif
 
-#define ISCSI_VERSION_STRING   "1.0.2/0.4.17r214" ISCSI_VERSION_STRING_SUFFIX
+#define ISCSI_VERSION_STRING   "2.0.0/0.4.17r214" ISCSI_VERSION_STRING_SUFFIX
index c7f7144..a3ba5c8 100644 (file)
@@ -15,6 +15,9 @@
 
 #include "iscsi.h"
 
+/* Protected by target_mgmt_mutex */
+int ctr_open_state;
+
 #ifdef CONFIG_SCST_PROC
 
 #include <linux/proc_fs.h>
@@ -61,7 +64,8 @@ static ssize_t iscsi_proc_log_entry_write(struct file *file,
        TRACE_EXIT_RES(res);
        return res;
 }
-#endif
+
+#endif /* DEBUG or TRACE */
 
 static int iscsi_version_info_show(struct seq_file *seq, void *v)
 {
@@ -230,6 +234,9 @@ err:
 
 #else /* CONFIG_SCST_PROC */
 
+/* Protected by target_mgmt_mutex */
+static LIST_HEAD(iscsi_attrs_list);
+
 static ssize_t iscsi_version_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf)
 {
@@ -260,93 +267,236 @@ static ssize_t iscsi_version_show(struct kobject *kobj,
 static struct kobj_attribute iscsi_version_attr =
        __ATTR(version, S_IRUGO, iscsi_version_show, NULL);
 
+static ssize_t iscsi_open_state_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       switch (ctr_open_state) {
+       case ISCSI_CTR_OPEN_STATE_CLOSED:
+               sprintf(buf, "%s\n", "closed");
+               break;
+       case ISCSI_CTR_OPEN_STATE_OPEN:
+               sprintf(buf, "%s\n", "open");
+               break;
+       case ISCSI_CTR_OPEN_STATE_CLOSING:
+               sprintf(buf, "%s\n", "closing");
+               break;
+       default:
+               sprintf(buf, "%s\n", "unknown");
+               break;
+       }
+
+       return strlen(buf);
+}
+
+static struct kobj_attribute iscsi_open_state_attr =
+       __ATTR(open_state, S_IRUGO, iscsi_open_state_show, NULL);
+
 const struct attribute *iscsi_attrs[] = {
        &iscsi_version_attr.attr,
+       &iscsi_open_state_attr.attr,
        NULL,
 };
 
 #endif /* CONFIG_SCST_PROC */
 
-/* target_mutex supposed to be locked */
-static int add_conn(struct iscsi_target *target, void __user *ptr)
+/* target_mgmt_mutex supposed to be locked */
+static int add_conn(void __user *ptr)
 {
        int err;
        struct iscsi_session *session;
        struct iscsi_kern_conn_info info;
+       struct iscsi_target *target;
+
+       TRACE_ENTRY();
 
        err = copy_from_user(&info, ptr, sizeof(info));
        if (err < 0)
-               return err;
+               goto out;
+
+       target = target_lookup_by_id(info.tid);
+       if (target == NULL) {
+               PRINT_ERROR("Target %d not found", info.tid);
+               err = -ENOENT;
+               goto out;
+       }
+
+       mutex_lock(&target->target_mutex);
 
        session = session_lookup(target, info.sid);
-       if (!session)
-               return -ENOENT;
+       if (!session) {
+               PRINT_ERROR("Session %lld not found",
+                       (long long unsigned int)info.tid);
+               err = -ENOENT;
+               goto out_unlock;
+       }
 
-       return conn_add(session, &info);
+       err = __add_conn(session, &info);
+
+out_unlock:
+       mutex_unlock(&target->target_mutex);
+
+out:
+       TRACE_EXIT_RES(err);
+       return err;
 }
 
-/* target_mutex supposed to be locked */
-static int del_conn(struct iscsi_target *target, void __user *ptr)
+/* target_mgmt_mutex supposed to be locked */
+static int del_conn(void __user *ptr)
 {
        int err;
        struct iscsi_session *session;
        struct iscsi_kern_conn_info info;
+       struct iscsi_target *target;
+
+       TRACE_ENTRY();
 
        err = copy_from_user(&info, ptr, sizeof(info));
        if (err < 0)
-               return err;
+               goto out;
+
+       target = target_lookup_by_id(info.tid);
+       if (target == NULL) {
+               PRINT_ERROR("Target %d not found", info.tid);
+               err = -ENOENT;
+               goto out;
+       }
+
+       mutex_lock(&target->target_mutex);
 
        session = session_lookup(target, info.sid);
        if (!session) {
                PRINT_ERROR("Session %llx not found",
                        (long long unsigned int)info.sid);
-               return -ENOENT;
+               err = -ENOENT;
+               goto out_unlock;
        }
 
-       return conn_del(session, &info);
+       err = __del_conn(session, &info);
+
+out_unlock:
+       mutex_unlock(&target->target_mutex);
+
+out:
+       TRACE_EXIT_RES(err);
+       return err;
 }
 
 /* target_mgmt_mutex supposed to be locked */
-static int add_session(struct iscsi_target *target, void __user *ptr)
+static int add_session(void __user *ptr)
 {
        int err;
-       struct iscsi_kern_session_info info;
+       struct iscsi_kern_session_info *info;
+       struct iscsi_target *target;
 
-       err = copy_from_user(&info, ptr, sizeof(info));
-       if (err < 0)
-               return err;
+       TRACE_ENTRY();
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (info == NULL) {
+               PRINT_ERROR("Can't alloc info (size %d)", sizeof(*info));
+               err = -ENOMEM;
+               goto out;
+       }
 
-       info.initiator_name[ISCSI_NAME_LEN-1] = '\0';
-       info.user_name[ISCSI_NAME_LEN-1] = '\0';
+       err = copy_from_user(info, ptr, sizeof(*info));
+       if (err != 0) {
+               PRINT_ERROR("copy_from_user() didn't copy %d bytes", err);
+               err = -EFAULT;
+               goto out_free;
+       }
 
-       return session_add(target, &info);
+       info->initiator_name[sizeof(info->initiator_name)-1] = '\0';
+#ifdef CONFIG_SCST_PROC
+       info->user_name[sizeof(info->user_name)-1] = '\0'; 
+#endif
+
+       target = target_lookup_by_id(info->tid);
+       if (target == NULL) {
+               PRINT_ERROR("Target %d not found", info->tid);
+               err = -ENOENT;
+               goto out_free;
+       }
+
+       err = __add_session(target, info);
+
+out_free:
+       kfree(info);
+
+out:
+       TRACE_EXIT_RES(err);
+       return err;
 }
 
-/* target_mutex supposed to be locked */
-static int del_session(struct iscsi_target *target, void __user *ptr)
+/* target_mgmt_mutex supposed to be locked */
+static int del_session(void __user *ptr)
 {
        int err;
-       struct iscsi_kern_session_info info;
+       struct iscsi_kern_session_info *info;
+       struct iscsi_target *target;
 
-       err = copy_from_user(&info, ptr, sizeof(info));
-       if (err < 0)
-               return err;
+       TRACE_ENTRY();
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (info == NULL) {
+               PRINT_ERROR("Can't alloc info (size %d)", sizeof(*info));
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = copy_from_user(info, ptr, sizeof(*info));
+       if (err != 0) {
+               PRINT_ERROR("copy_from_user() didn't copy %d bytes", err);
+               err = -EFAULT;
+               goto out_free;
+       }
+
+       info->initiator_name[sizeof(info->initiator_name)-1] = '\0';
+#ifdef CONFIG_SCST_PROC
+       info->user_name[sizeof(info->user_name)-1] = '\0'; 
+#endif
+
+       target = target_lookup_by_id(info->tid);
+       if (target == NULL) {
+               PRINT_ERROR("Target %d not found", info->tid);
+               err = -ENOENT;
+               goto out_free;
+       }
 
-       return session_del(target, info.sid);
+       mutex_lock(&target->target_mutex);
+       err = __del_session(target, info->sid);
+       mutex_unlock(&target->target_mutex);
+
+out_free:
+       kfree(info);
+
+out:
+       TRACE_EXIT_RES(err);
+       return err;
 }
 
-/* target_mutex supposed to be locked */
-static int iscsi_param_config(struct iscsi_target *target, void __user *ptr,
-                             int set)
+/* target_mgmt_mutex supposed to be locked */
+static int iscsi_params_config(void __user *ptr, int set)
 {
        int err;
-       struct iscsi_kern_param_info info;
+       struct iscsi_kern_params_info info;
+       struct iscsi_target *target;
+
+       TRACE_ENTRY();
 
        err = copy_from_user(&info, ptr, sizeof(info));
        if (err < 0)
                goto out;
 
-       err = iscsi_param_set(target, &info, set);
+       target = target_lookup_by_id(info.tid);
+       if (target == NULL) {
+               PRINT_ERROR("Target %d not found", info.tid);
+               err = -ENOENT;
+               goto out;
+       }
+
+       mutex_lock(&target->target_mutex);
+       err = iscsi_params_set(target, &info, set);
+       mutex_unlock(&target->target_mutex);
+
        if (err < 0)
                goto out;
 
@@ -354,72 +504,473 @@ static int iscsi_param_config(struct iscsi_target *target, void __user *ptr,
                err = copy_to_user(ptr, &info, sizeof(info));
 
 out:
+       TRACE_EXIT_RES(err);
        return err;
 }
 
+#ifndef CONFIG_SCST_PROC
+
 /* target_mgmt_mutex supposed to be locked */
-static int enable_target(void __user *ptr)
+static int mgmt_cmd_callback(void __user *ptr)
 {
-       int err;
-       struct iscsi_kern_target_info info;
+       int err = 0, rc;
+       struct iscsi_kern_mgmt_cmd_res_info cinfo;
+       struct scst_sysfs_user_info *info;
 
-       err = copy_from_user(&info, ptr, sizeof(info));
-       if (err < 0)
-               return err;
+       TRACE_ENTRY();
+
+       rc = copy_from_user(&cinfo, ptr, sizeof(cinfo));
+       if (rc != 0) {
+               err = -EFAULT;
+               goto out;
+       }
+
+       cinfo.value[sizeof(cinfo.value)-1] = '\0';
+
+       info = scst_sysfs_user_get_info(cinfo.cookie);
+       TRACE_DBG("cookie %u, info %p, result %d", cinfo.cookie, info,
+               cinfo.result);
+       if (info == NULL) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       info->info_status = 0;
+
+       if (cinfo.result != 0) {
+               info->info_status = cinfo.result;
+               goto out_complete;
+       }
+
+       switch (cinfo.req_cmd) {
+       case E_ENABLE_TARGET:
+       case E_DISABLE_TARGET:
+       {
+               struct iscsi_target *target;
+
+               target = target_lookup_by_id(cinfo.tid);
+               if (target == NULL) {
+                       PRINT_ERROR("Target %d not found", cinfo.tid);
+                       err = -ENOENT;
+                       goto out_status;
+               }
 
-       err = target_enable(&info);
+               target->tgt_enabled = (cinfo.req_cmd == E_ENABLE_TARGET) ? 1 : 0;
+               break;
+       }
+
+       case E_GET_ATTR_VALUE:
+               info->data = kstrdup(cinfo.value, GFP_KERNEL);
+               if (info->data == NULL) {
+                       PRINT_ERROR("Can't dublicate value %s", cinfo.value);
+                       info->info_status = -ENOMEM;
+                       goto out_complete;
+               }
+               break;
+       }
 
+out_complete:
+       complete(&info->info_completion);
+
+out:
+       TRACE_EXIT_RES(err);
        return err;
+
+out_status:
+       info->info_status = err;
+       goto out_complete;
+}
+
+static ssize_t iscsi_attr_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       int pos;
+       struct iscsi_attr *tgt_attr;
+       void *value;
+
+       TRACE_ENTRY();
+
+       tgt_attr = container_of(attr, struct iscsi_attr, attr);
+
+       pos = iscsi_sysfs_send_event(
+               (tgt_attr->target != NULL) ? tgt_attr->target->tid : 0,
+               E_GET_ATTR_VALUE, tgt_attr->name, NULL, &value);
+
+       if (pos != 0)
+               goto out;
+
+       pos = scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, "%s\n", (char *)value);
+
+       kfree(value);
+
+out:
+       TRACE_EXIT_RES(pos);
+       return pos;
+}
+
+static ssize_t iscsi_attr_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       int res;
+       char *buffer;
+       struct iscsi_attr *tgt_attr;
+
+       TRACE_ENTRY();
+
+       buffer = kzalloc(count+1, GFP_KERNEL);
+       if (buffer == NULL) {
+               res = -ENOMEM;
+               goto out;
+       }
+
+       memcpy(buffer, buf, count);
+       buffer[count] = '\0';
+
+       tgt_attr = container_of(attr, struct iscsi_attr, attr);
+
+       res = iscsi_sysfs_send_event(
+               (tgt_attr->target != NULL) ? tgt_attr->target->tid : 0,
+               E_SET_ATTR_VALUE, tgt_attr->name, buffer, NULL);
+
+       kfree(buffer);
+
+       if (res == 0)
+               res = count;
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+/*
+ * target_mgmt_mutex supposed to be locked. If target != 0, target_mutex
+ * supposed to be locked as well.
+ */
+int iscsi_add_attr(struct iscsi_target *target,
+       const struct iscsi_kern_attr *attr_info)
+{
+       int res = 0;
+       struct iscsi_attr *tgt_attr;
+       struct list_head *attrs_list;
+       const char *name;
+
+       TRACE_ENTRY();
+
+       if (target != NULL) {
+               attrs_list = &target->attrs_list;
+               name = target->name;
+       } else {
+               attrs_list = &iscsi_attrs_list;
+               name = "global";
+       }
+
+       list_for_each_entry(tgt_attr, attrs_list, attrs_list_entry) {
+               if (strncmp(tgt_attr->name, attr_info->name,
+                               sizeof(tgt_attr->name) == 0)) {
+                       PRINT_ERROR("Attribute %s for %s already exist",
+                               attr_info->name, name);
+                       res = -EEXIST;
+                       goto out;
+               }
+       }
+
+       TRACE_DBG("Adding %s's attr %s with mode %x", name,
+               attr_info->name, attr_info->mode);
+
+       tgt_attr = kzalloc(sizeof(*tgt_attr), GFP_KERNEL);
+       if (tgt_attr == NULL) {
+               PRINT_ERROR("Unable to allocate user (size %d)",
+                       sizeof(*tgt_attr));
+               res = -ENOMEM;
+               goto out;
+       }
+
+       tgt_attr->target = target;
+
+       tgt_attr->name = kstrdup(attr_info->name, GFP_KERNEL);
+       if (tgt_attr->name == NULL) {
+               PRINT_ERROR("Unable to allocate attr %s name/value (target %s)",
+                       attr_info->name, name);
+               res = -ENOMEM;
+               goto out_free;
+       }
+
+       list_add(&tgt_attr->attrs_list_entry, attrs_list);
+
+       tgt_attr->attr.attr.name = tgt_attr->name;
+       tgt_attr->attr.attr.owner = THIS_MODULE;
+       tgt_attr->attr.attr.mode = attr_info->mode & (S_IRUGO | S_IWUGO);
+       tgt_attr->attr.show = iscsi_attr_show;
+       tgt_attr->attr.store = iscsi_attr_store;
+
+       res = sysfs_create_file(
+               (target != NULL) ? scst_sysfs_get_tgt_kobj(target->scst_tgt) :
+                               scst_sysfs_get_tgtt_kobj(&iscsi_template),
+               &tgt_attr->attr.attr);
+       if (res != 0) {
+               PRINT_ERROR("Unable to create file '%s' for target '%s'",
+                       tgt_attr->attr.attr.name, name);
+               goto out_del;
+       }
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+out_del:
+       list_del(&tgt_attr->attrs_list_entry);
+
+out_free:
+       kfree(tgt_attr->name);
+       kfree(tgt_attr);
+       goto out;
+}
+
+void __iscsi_del_attr(struct iscsi_target *target,
+       struct iscsi_attr *tgt_attr)
+{
+       TRACE_ENTRY();
+
+       TRACE_DBG("Deleting %s's attr %s",
+               (target != NULL) ? target->name : "global", tgt_attr->name);
+
+       list_del(&tgt_attr->attrs_list_entry);
+
+       sysfs_remove_file((target != NULL) ?
+                       scst_sysfs_get_tgt_kobj(target->scst_tgt) :
+                       scst_sysfs_get_tgtt_kobj(&iscsi_template),
+               &tgt_attr->attr.attr);
+
+       kfree(tgt_attr->name);
+       kfree(tgt_attr);
+
+       TRACE_EXIT();
+       return;
+}
+
+/*
+ * target_mgmt_mutex supposed to be locked. If target != 0, target_mutex
+ * supposed to be locked as well.
+ */
+static int iscsi_del_attr(struct iscsi_target *target,
+       const char *attr_name)
+{
+       int res = 0;
+       struct iscsi_attr *tgt_attr, *a;
+       struct list_head *attrs_list;
+
+       TRACE_ENTRY();
+
+       if (target != NULL)
+               attrs_list = &target->attrs_list;
+       else
+               attrs_list = &iscsi_attrs_list;
+
+       tgt_attr = NULL;
+       list_for_each_entry(a, attrs_list, attrs_list_entry) {
+               if (strncmp(a->name, attr_name, sizeof(a->name)) == 0) {
+                       tgt_attr = a;
+                       break;
+               }
+       }
+
+       if (tgt_attr == NULL) {
+               PRINT_ERROR("attr %s not found (target %s)", attr_name,
+                       (target != NULL) ? target->name : "global");
+               res = -ENOENT;
+               goto out;
+       }
+
+       __iscsi_del_attr(target, tgt_attr);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
 }
 
 /* target_mgmt_mutex supposed to be locked */
-static int disable_target(void __user *ptr)
+static int iscsi_attr_cmd(void __user *ptr, unsigned int cmd)
 {
-       int err;
-       struct iscsi_kern_target_info info;
+       int rc, err = 0;
+       struct iscsi_kern_attr_info info;
+       struct iscsi_target *target;
+       struct scst_sysfs_user_info *i = NULL;
 
-       err = copy_from_user(&info, ptr, sizeof(info));
-       if (err < 0)
-               return err;
+       TRACE_ENTRY();
 
-       err = target_disable(&info);
+       rc = copy_from_user(&info, ptr, sizeof(info));
+       if (rc != 0) {
+               err = -EFAULT;
+               goto out;
+       }
+
+       info.attr.name[sizeof(info.attr.name)-1] = '\0';
 
+       if (info.cookie != 0) {
+               i = scst_sysfs_user_get_info(info.cookie);
+               TRACE_DBG("cookie %u, uinfo %p", info.cookie, i);
+               if (i == NULL) {
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+       target = target_lookup_by_id(info.tid);
+
+       if (target != NULL)
+               mutex_lock(&target->target_mutex);
+
+       switch (cmd) {
+       case ISCSI_ATTR_ADD:
+               err = iscsi_add_attr(target, &info.attr);
+               break;
+       case ISCSI_ATTR_DEL:
+               err = iscsi_del_attr(target, info.attr.name);
+               break;
+       default:
+               sBUG();
+       }
+
+       if (target != NULL)
+               mutex_unlock(&target->target_mutex);
+
+       if (i != NULL) {
+               i->info_status = err;
+               complete(&i->info_completion);
+       }
+
+out:
+       TRACE_EXIT_RES(err);
        return err;
 }
 
+#endif /* CONFIG_SCST_PROC */
+
 /* target_mgmt_mutex supposed to be locked */
 static int add_target(void __user *ptr)
+{
+       int err;
+       struct iscsi_kern_target_info *info;
+#ifndef CONFIG_SCST_PROC
+       struct scst_sysfs_user_info *uinfo;
+#endif
+
+       TRACE_ENTRY();
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (info == NULL) {
+               PRINT_ERROR("Can't alloc info (size %d)", sizeof(*info));
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = copy_from_user(info, ptr, sizeof(*info));
+       if (err != 0) {
+               PRINT_ERROR("copy_from_user() didn't copy %d bytes", err);
+               err = -EFAULT;
+               goto out_free;
+       }
+
+       if (target_lookup_by_id(info->tid) != NULL) {
+               PRINT_ERROR("Target %u already exist!", info->tid);
+               err = -EEXIST;
+               goto out_free;
+       }
+
+       info->name[sizeof(info->name)-1] = '\0';
+
+#ifndef CONFIG_SCST_PROC
+       if (info->cookie != 0) {
+               uinfo = scst_sysfs_user_get_info(info->cookie);
+               TRACE_DBG("cookie %u, uinfo %p", info->cookie, uinfo);
+               if (uinfo == NULL) {
+                       err = -EINVAL;
+                       goto out_free;
+               }
+       } else
+               uinfo = NULL;
+#endif
+
+       err = __add_target(info);
+
+#ifndef CONFIG_SCST_PROC
+       if (uinfo != NULL) {
+               uinfo->info_status = err;
+               complete(&uinfo->info_completion);
+       }
+#endif
+
+out_free:
+       kfree(info);
+
+out:
+       TRACE_EXIT_RES(err);
+       return err;
+}
+
+/* target_mgmt_mutex supposed to be locked */
+static int del_target(void __user *ptr)
 {
        int err;
        struct iscsi_kern_target_info info;
+#ifndef CONFIG_SCST_PROC
+       struct scst_sysfs_user_info *uinfo;
+#endif
+
+       TRACE_ENTRY();
 
        err = copy_from_user(&info, ptr, sizeof(info));
        if (err < 0)
-               return err;
+               goto out;
 
-       err = target_add(&info);
-       if (!err)
-               err = copy_to_user(ptr, &info, sizeof(info));
+       info.name[sizeof(info.name)-1] = '\0';
+
+#ifndef CONFIG_SCST_PROC
+       if (info.cookie != 0) {
+               uinfo = scst_sysfs_user_get_info(info.cookie);
+               TRACE_DBG("cookie %u, uinfo %p", info.cookie, uinfo);
+               if (uinfo == NULL) {
+                       err = -EINVAL;
+                       goto out;
+               }
+       } else
+               uinfo = NULL;
+#endif
+
+       err = __del_target(info.tid);
+
+#ifndef CONFIG_SCST_PROC
+       if (uinfo != NULL) {
+               uinfo->info_status = err;
+               complete(&uinfo->info_completion);
+       }
+#endif
 
+out:
+       TRACE_EXIT_RES(err);
        return err;
 }
 
-static int iscsi_check_version(void __user *arg)
+static int iscsi_register(void __user *arg)
 {
        struct iscsi_kern_register_info reg;
        char ver[sizeof(ISCSI_SCST_INTERFACE_VERSION)+1];
-       int res;
+       int res, rc;
+
+       TRACE_ENTRY();
 
-       res = copy_from_user(&reg, arg, sizeof(reg));
-       if (res < 0) {
+       rc = copy_from_user(&reg, arg, sizeof(reg));
+       if (rc != 0) {
                PRINT_ERROR("%s", "Unable to get register info");
+               res = -EFAULT;
                goto out;
        }
 
-       res = copy_from_user(ver, (void __user *)(unsigned long)reg.version,
+       rc = copy_from_user(ver, (void __user *)(unsigned long)reg.version,
                                sizeof(ver));
-       if (res < 0) {
+       if (rc < 0) {
                PRINT_ERROR("%s", "Unable to get version string");
+               res = -EFAULT;
                goto out;
        }
        ver[sizeof(ver)-1] = '\0';
@@ -431,125 +982,141 @@ static int iscsi_check_version(void __user *arg)
                goto out;
        }
 
-       res = ISCSI_CONN_IOV_MAX << PAGE_SHIFT;
+       memset(&reg, 0, sizeof(reg));
+       reg.max_data_seg_len = ISCSI_CONN_IOV_MAX << PAGE_SHIFT;
+
+       res = 0;
+
+       rc = copy_to_user(arg, &reg, sizeof(reg));
+       if (rc != 0) {
+               PRINT_ERROR("copy_to_user() failed to copy %d bytes", res);
+               res = -EFAULT;
+               goto out;
+       }
 
 out:
+       TRACE_EXIT_RES(res);
        return res;
 }
 
 static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-       struct iscsi_target *target = NULL;
        long err;
-       u32 id;
-
-       switch (cmd) {
-       case ADD_TARGET:
-       case DEL_TARGET:
-       case ENABLE_TARGET:
-       case DISABLE_TARGET:
-       case ADD_SESSION:
-       case DEL_SESSION:
-       case ISCSI_PARAM_SET:
-       case ISCSI_PARAM_GET:
-       case ADD_CONN:
-       case DEL_CONN:
-               break;
 
-       case REGISTER_USERD:
-               err = iscsi_check_version((void __user *) arg);
-               goto out;
+       TRACE_ENTRY();
 
-       default:
-               PRINT_ERROR("Invalid ioctl cmd %x", cmd);
-               err = -EINVAL;
+       if (cmd == REGISTER_USERD) {
+               err = iscsi_register((void __user *)arg);
                goto out;
        }
 
-       err = get_user(id, (u32 __user *) arg);
-       if (err != 0)
-               goto out;
-
        err = mutex_lock_interruptible(&target_mgmt_mutex);
        if (err < 0)
                goto out;
 
-       if (cmd == DEL_TARGET) {
-               err = target_del(id);
-               goto out_unlock;
-       }
-
-       target = target_lookup_by_id(id);
-
-       if (cmd == ADD_TARGET)
-               if (target) {
-                       err = -EEXIST;
-                       PRINT_ERROR("Target %u already exist!", id);
-                       goto out_unlock;
-               }
-
        switch (cmd) {
        case ADD_TARGET:
-               err = add_target((void __user *) arg);
-               goto out_unlock;
-       case ENABLE_TARGET:
-               err = enable_target((void __user *) arg);
-               goto out_unlock;
-       case DISABLE_TARGET:
-               err = disable_target((void __user *) arg);
-               goto out_unlock;
-       case ADD_SESSION:
-               err = add_session(target, (void __user *) arg);
-               goto out_unlock;
-       }
+               err = add_target((void __user *)arg);
+               break;
 
-       if (!target) {
-               PRINT_ERROR("Can't find the target %u", id);
-               err = -EINVAL;
-               goto out_unlock;
-       }
+       case DEL_TARGET:
+               err = del_target((void __user *)arg);
+               break;
 
-       mutex_lock(&target->target_mutex);
+#ifndef CONFIG_SCST_PROC
+       case ISCSI_ATTR_ADD:
+       case ISCSI_ATTR_DEL:
+               err = iscsi_attr_cmd((void __user *)arg, cmd);
+               break;
+
+       case MGMT_CMD_CALLBACK:
+               err = mgmt_cmd_callback((void __user *)arg);
+               break;
+#endif
+
+       case ADD_SESSION:
+               err = add_session((void __user *)arg);
+               break;
 
-       switch (cmd) {
        case DEL_SESSION:
-               err = del_session(target, (void __user *) arg);
+               err = del_session((void __user *)arg);
                break;
 
        case ISCSI_PARAM_SET:
-               err = iscsi_param_config(target, (void __user *) arg, 1);
+               err = iscsi_params_config((void __user *)arg, 1);
                break;
 
        case ISCSI_PARAM_GET:
-               err = iscsi_param_config(target, (void __user *) arg, 0);
+               err = iscsi_params_config((void __user *)arg, 0);
                break;
 
        case ADD_CONN:
-               err = add_conn(target, (void __user *) arg);
+               err = add_conn((void __user *)arg);
                break;
 
        case DEL_CONN:
-               err = del_conn(target, (void __user *) arg);
+               err = del_conn((void __user *)arg);
                break;
 
        default:
-               sBUG();
-               break;
+               PRINT_ERROR("Invalid ioctl cmd %x", cmd);
+               err = -EINVAL;
+               goto out_unlock;
        }
 
-       mutex_unlock(&target->target_mutex);
-
 out_unlock:
        mutex_unlock(&target_mgmt_mutex);
 
 out:
+       TRACE_EXIT_RES(err);
        return err;
 }
 
+int open(struct inode *inode, struct file *file)
+{
+       bool already;
+
+       mutex_lock(&target_mgmt_mutex);
+       already = (ctr_open_state != ISCSI_CTR_OPEN_STATE_CLOSED);
+       if (!already)
+               ctr_open_state = ISCSI_CTR_OPEN_STATE_OPEN;
+       mutex_unlock(&target_mgmt_mutex);
+
+       if (already) {
+               PRINT_WARNING("%s", "Attempt to second open the control "
+                       "device!");
+               return -EBUSY;
+       } else
+               return 0;
+}
+
 static int release(struct inode *inode, struct file *filp)
 {
+#ifndef CONFIG_SCST_PROC
+       struct iscsi_attr *attr, *t;
+#endif
+
        TRACE(TRACE_MGMT, "%s", "Releasing allocated resources");
+
+       mutex_lock(&target_mgmt_mutex);
+       ctr_open_state = ISCSI_CTR_OPEN_STATE_CLOSING;
+       mutex_unlock(&target_mgmt_mutex);
+
        target_del_all();
+
+       mutex_lock(&target_mgmt_mutex);
+
+#ifndef CONFIG_SCST_PROC
+       list_for_each_entry_safe(attr, t, &iscsi_attrs_list,
+                                       attrs_list_entry) {
+               __iscsi_del_attr(NULL, attr);
+       }
+#endif
+
+       ctr_open_state = ISCSI_CTR_OPEN_STATE_CLOSED;
+
+       mutex_unlock(&target_mgmt_mutex);
+
        return 0;
 }
 
@@ -557,6 +1124,7 @@ const struct file_operations ctr_fops = {
        .owner          = THIS_MODULE,
        .unlocked_ioctl = ioctl,
        .compat_ioctl   = ioctl,
+       .open           = open,
        .release        = release,
 };
 
index ca2f0ae..8a5eefe 100644 (file)
@@ -62,6 +62,20 @@ out:
 
 #ifdef CONFIG_SCST_PROC
 
+static int print_digest_state(char *p, size_t size, unsigned long flags)
+{
+       int pos;
+
+       if (DIGEST_NONE & flags)
+               pos = scnprintf(p, size, "%s", "none");
+       else if (DIGEST_CRC32C & flags)
+               pos = scnprintf(p, size, "%s", "crc32c");
+       else
+               pos = scnprintf(p, size, "%s", "unknown");
+
+       return pos;
+}
+
 /* target_mutex supposed to be locked */
 void conn_info_show(struct seq_file *seq, struct iscsi_session *session)
 {
@@ -666,8 +680,8 @@ static int iscsi_conn_alloc(struct iscsi_session *session,
        conn->rd_state = ISCSI_CONN_RD_STATE_IDLE;
        conn->wr_state = ISCSI_CONN_WR_STATE_IDLE;
 
-       conn->hdigest_type = session->sess_param.header_digest;
-       conn->ddigest_type = session->sess_param.data_digest;
+       conn->hdigest_type = session->sess_params.header_digest;
+       conn->ddigest_type = session->sess_params.data_digest;
        res = digest_init(conn);
        if (res != 0)
                goto out_err_free1;
@@ -770,7 +784,7 @@ out_err:
 }
 
 /* target_mutex supposed to be locked */
-int conn_add(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
+int __add_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
 {
        struct iscsi_conn *conn, *new_conn = NULL;
        int err;
@@ -805,7 +819,7 @@ out:
 }
 
 /* target_mutex supposed to be locked */
-int conn_del(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
+int __del_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
 {
        struct iscsi_conn *conn;
        int err = -EEXIST;
index 4453fde..d450dea 100644 (file)
@@ -86,36 +86,88 @@ static void event_recv(struct sock *sk, int length)
 }
 #endif
 
-static int notify(void *data, int len)
+/* event_mutex supposed to be held */
+static int __event_send(const void *buf, int buf_len)
 {
+       int res = 0, len;
        struct sk_buff *skb;
        struct nlmsghdr *nlh;
-       static u32 seq;
+       static u32 seq; /* protected by event_mutex */
+
+       TRACE_ENTRY();
+
+       if (ctr_open_state != ISCSI_CTR_OPEN_STATE_OPEN)
+               goto out;
+
+       len = NLMSG_SPACE(buf_len);
 
        skb = alloc_skb(NLMSG_SPACE(len), GFP_KERNEL);
-       if (!skb)
-               return -ENOMEM;
+       if (skb == NULL) {
+               PRINT_ERROR("alloc_skb() failed (len %d)", len);
+               res =  -ENOMEM;
+               goto out;
+       }
 
        nlh = __nlmsg_put(skb, iscsid_pid, seq++, NLMSG_DONE,
                          len - sizeof(*nlh), 0);
 
-       memcpy(NLMSG_DATA(nlh), data, len);
+       memcpy(NLMSG_DATA(nlh), buf, buf_len);
+       res = netlink_unicast(nl, skb, iscsid_pid, 0);
+       if (res <= 0) {
+               if (res != -ECONNREFUSED)
+                       PRINT_ERROR("netlink_unicast() failed: %d", res);
+               else
+                       TRACE(TRACE_MINOR, "netlink_unicast() failed: %s. "
+                               "Not functioning user space?",
+                               "Connection refused");
+               goto out;
+       }
 
-       return netlink_unicast(nl, skb, iscsid_pid, 0);
+out:
+       TRACE_EXIT_RES(res);
+       return res;
 }
 
-int event_send(u32 tid, u64 sid, u32 cid, enum iscsi_kern_event_code code)
+int event_send(u32 tid, u64 sid, u32 cid, u32 cookie,
+       enum iscsi_kern_event_code code,
+       const char *param1, const char *param2)
 {
        int err;
+       static DEFINE_MUTEX(event_mutex);
        struct iscsi_kern_event event;
+       int param1_size, param2_size;
+
+       param1_size = (param1 != NULL) ? strlen(param1) : 0;
+       param2_size = (param2 != NULL) ? strlen(param2) : 0;
 
        event.tid = tid;
        event.sid = sid;
        event.cid = cid;
        event.code = code;
+       event.cookie = cookie;
+       event.param1_size = param1_size;
+       event.param2_size = param2_size;
+
+       mutex_lock(&event_mutex);
+
+       err = __event_send(&event, sizeof(event));
+       if (err <= 0)
+               goto out_unlock;
 
-       err = notify(&event, NLMSG_SPACE(sizeof(struct iscsi_kern_event)));
+       if (param1_size > 0) {
+               err = __event_send(param1, param1_size);
+               if (err <= 0)
+                       goto out_unlock;
+       }
+
+       if (param2_size > 0) {
+               err = __event_send(param2, param2_size);
+               if (err <= 0)
+                       goto out_unlock;
+       }
 
+out_unlock:
+       mutex_unlock(&event_mutex);
        return err;
 }
 
index 826c0e3..68ec278 100644 (file)
@@ -37,7 +37,6 @@
 
 static int ctr_major;
 static char ctr_name[] = "iscsi-scst-ctl";
-static int iscsi_template_registered;
 
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
 unsigned long iscsi_trace_flag = ISCSI_DEFAULT_LOG_FLAGS;
@@ -725,7 +724,7 @@ static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status)
 
        TRACE_DBG("req %p", req);
 
-       pdusize = req->conn->session->sess_param.max_xmit_data_length;
+       pdusize = req->conn->session->sess_params.max_xmit_data_length;
        expsize = req->read_size;
        size = min(expsize, (u32)req->bufflen);
        offset = 0;
@@ -1007,7 +1006,7 @@ out:
 
 static inline int iscsi_get_allowed_cmds(struct iscsi_session *sess)
 {
-       int res = max(-1, (int)sess->max_queued_cmnds -
+       int res = max(-1, (int)sess->tgt_params.queued_cmnds -
                                atomic_read(&sess->active_cmds)-1);
        TRACE_DBG("allowed cmds %d (sess %p, active_cmds %d)", res,
                sess, atomic_read(&sess->active_cmds));
@@ -1461,12 +1460,12 @@ static void send_r2t(struct iscsi_cmnd *req)
         */
 
        EXTRACHECKS_BUG_ON(req->outstanding_r2t >
-                          sess->sess_param.max_outstanding_r2t);
+                          sess->sess_params.max_outstanding_r2t);
 
-       if (req->outstanding_r2t == sess->sess_param.max_outstanding_r2t)
+       if (req->outstanding_r2t == sess->sess_params.max_outstanding_r2t)
                goto out;
 
-       burst = sess->sess_param.max_burst_length;
+       burst = sess->sess_params.max_burst_length;
        offset = be32_to_cpu(cmnd_hdr(req)->data_length) -
                        req->r2t_len_to_send;
 
@@ -1498,7 +1497,7 @@ static void send_r2t(struct iscsi_cmnd *req)
                list_add_tail(&rsp->write_list_entry, &send);
                req->outstanding_r2t++;
 
-       } while ((req->outstanding_r2t < sess->sess_param.max_outstanding_r2t) &&
+       } while ((req->outstanding_r2t < sess->sess_params.max_outstanding_r2t) &&
                 (req->r2t_len_to_send != 0));
 
        iscsi_cmnds_init_write(&send, ISCSI_INIT_WRITE_WAKE);
@@ -1679,7 +1678,7 @@ int cmnd_rx_continue(struct iscsi_cmnd *req)
        if (dir & SCST_DATA_WRITE) {
                unsolicited_data_expected = !(req_hdr->flags & ISCSI_CMD_FINAL);
 
-               if (unlikely(session->sess_param.initial_r2t &&
+               if (unlikely(session->sess_params.initial_r2t &&
                    unsolicited_data_expected)) {
                        PRINT_ERROR("Initiator %s violated negotiated "
                                "parameters: initial R2T is required (ITT %x, "
@@ -1688,7 +1687,7 @@ int cmnd_rx_continue(struct iscsi_cmnd *req)
                        goto out_close;
                }
 
-               if (unlikely(!session->sess_param.immediate_data &&
+               if (unlikely(!session->sess_params.immediate_data &&
                    req->pdu.datasize)) {
                        PRINT_ERROR("Initiator %s violated negotiated "
                                "parameters: forbidden immediate data sent "
@@ -1697,13 +1696,13 @@ int cmnd_rx_continue(struct iscsi_cmnd *req)
                        goto out_close;
                }
 
-               if (unlikely(session->sess_param.first_burst_length < req->pdu.datasize)) {
+               if (unlikely(session->sess_params.first_burst_length < req->pdu.datasize)) {
                        PRINT_ERROR("Initiator %s violated negotiated "
                                "parameters: immediate data len (%d) > "
                                "first_burst_length (%d) (ITT %x, op  %x)",
                                session->initiator_name,
                                req->pdu.datasize,
-                               session->sess_param.first_burst_length,
+                               session->sess_params.first_burst_length,
                                cmnd_itt(req), req_hdr->scb[0]);
                        goto out_close;
                }
@@ -1732,7 +1731,7 @@ int cmnd_rx_continue(struct iscsi_cmnd *req)
                        req->outstanding_r2t = 1;
                        req->r2t_len_to_send = req->r2t_len_to_receive -
                                min_t(unsigned int,
-                                     session->sess_param.first_burst_length -
+                                     session->sess_params.first_burst_length -
                                                req->pdu.datasize,
                                      req->r2t_len_to_receive);
                } else
@@ -2853,12 +2852,12 @@ static int check_segment_length(struct iscsi_cmnd *cmnd)
        struct iscsi_conn *conn = cmnd->conn;
        struct iscsi_session *session = conn->session;
 
-       if (unlikely(cmnd->pdu.datasize > session->sess_param.max_recv_data_length)) {
+       if (unlikely(cmnd->pdu.datasize > session->sess_params.max_recv_data_length)) {
                PRINT_ERROR("Initiator %s violated negotiated parameters: "
                        "data too long (ITT %x, datasize %u, "
                        "max_recv_data_length %u", session->initiator_name,
                        cmnd_itt(cmnd), cmnd->pdu.datasize,
-                       session->sess_param.max_recv_data_length);
+                       session->sess_params.max_recv_data_length);
                mark_conn_closed(conn);
                return -EINVAL;
        }
@@ -3463,6 +3462,16 @@ static struct scst_trace_log iscsi_local_trace_tbl[] = {
 #define ISCSI_TRACE_TLB_HELP   ", d_read, d_write, conn, conn_dbg, iov, pdu, net_page"
 #endif
 
+#define ISCSI_MGMT_CMD_HELP    \
+       "       echo \"add_attribute IncomingUser name password\" >mgmt\n" \
+       "       echo \"del_attribute IncomingUser name\" >mgmt\n" \
+       "       echo \"add_attribute OutgoingUser name password\" >mgmt\n" \
+       "       echo \"del_attribute OutgoingUser name\" >mgmt\n" \
+       "       echo \"add_target_attribute target_name IncomingUser name password\" >mgmt\n" \
+       "       echo \"del_target_attribute target_name IncomingUser name\" >mgmt\n" \
+       "       echo \"add_target_attribute target_name OutgoingUser name password\" >mgmt\n" \
+       "       echo \"del_target_attribute target_name OutgoingUser name\" >mgmt\n"
+
 struct scst_tgt_template iscsi_template = {
        .name = "iscsi",
        .sg_tablesize = 0xFFFF /* no limit */,
@@ -3473,8 +3482,12 @@ struct scst_tgt_template iscsi_template = {
        .tgtt_attrs = iscsi_attrs,
        .tgt_attrs = iscsi_tgt_attrs,
        .sess_attrs = iscsi_sess_attrs,
-       .enable_tgt = iscsi_enable_target,
-       .is_tgt_enabled = iscsi_is_target_enabled,
+       .enable_target = iscsi_enable_target,
+       .is_target_enabled = iscsi_is_target_enabled,
+       .add_target = iscsi_sysfs_add_target,
+       .del_target = iscsi_sysfs_del_target,
+       .mgmt_cmd = iscsi_sysfs_mgmt_cmd,
+       .mgmt_cmd_help = ISCSI_MGMT_CMD_HELP,
 #endif
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
        .default_trace_flags = ISCSI_DEFAULT_LOG_FLAGS,
@@ -3594,8 +3607,6 @@ static int __init iscsi_init(void)
        if (err < 0)
                goto out_kmem;
 
-       iscsi_template_registered = 1;
-
 #ifdef CONFIG_SCST_PROC
        err = iscsi_procfs_init();
        if (err < 0)
index 89e9d73..d3d1571 100644 (file)
 #define iscsi_sense_unexpected_unsolicited_data        ABORTED_COMMAND, 0x0C, 0x0C
 #define iscsi_sense_incorrect_amount_of_data   ABORTED_COMMAND, 0x0C, 0x0D
 
-struct iscsi_sess_param {
+/*
+ * All members must have int type to match expectations of iscsi_tgt_store_*()
+ * functions!
+ */
+struct iscsi_sess_params {
        int initial_r2t;
        int immediate_data;
        int max_connections;
@@ -55,7 +59,11 @@ struct iscsi_sess_param {
        int ifmarkint;
 };
 
-struct iscsi_trgt_param {
+/*
+ * All members must have int type to match expectations of iscsi_tgt_store_*()
+ * functions!
+ */
+struct iscsi_tgt_params {
        int queued_cmnds;
 };
 
@@ -64,8 +72,18 @@ struct network_thread_info {
        unsigned int ready;
 };
 
+struct iscsi_target;
 struct iscsi_cmnd;
 
+#ifndef CONFIG_SCST_PROC
+struct iscsi_attr {
+       struct list_head attrs_list_entry;
+       struct kobj_attribute attr;
+       struct iscsi_target *target;
+       const char *name;
+};
+#endif
+
 struct iscsi_target {
        struct scst_tgt *scst_tgt;
 
@@ -73,24 +91,16 @@ struct iscsi_target {
 
        struct list_head session_list; /* protected by target_mutex */
 
-       /* Both protected by target_mgmt_mutex */
-       struct iscsi_trgt_param trgt_param;
-       /*
-        * Put here to have uniform parameters checking and assigning
-        * from various places, including iscsi-scst-adm.
-        */
-       struct iscsi_sess_param trgt_sess_param;
-
        struct list_head target_list_entry;
        u32 tid;
 
-       /* All protected by target_sysfs_mutex */
+       /* Protected by iscsi_sysfs_mutex */
        unsigned int tgt_enabled:1;
-       unsigned int expected_ioctl;
-       int ioctl_res;
-       struct completion *target_enabling_cmpl;
 
-       struct mutex target_sysfs_mutex;
+#ifndef CONFIG_SCST_PROC
+       /* Protected by target_mutex */
+       struct list_head attrs_list;
+#endif
 
        char name[ISCSI_NAME_LEN];
 };
@@ -107,7 +117,8 @@ struct iscsi_session {
        /* Unprotected, since accessed only from a single read thread */
        u32 next_ttt;
 
-       u32 max_queued_cmnds; /* unprotected, since read-only */
+       /* Read only, if there are connection(s) */
+       struct iscsi_tgt_params tgt_params;
        atomic_t active_cmds;
 
        spinlock_t sn_lock;
@@ -119,7 +130,7 @@ struct iscsi_session {
        struct iscsi_cmnd *tm_rsp;
 
        /* Read only, if there are connection(s) */
-       struct iscsi_sess_param sess_param;
+       struct iscsi_sess_params sess_params;
 
        /*
         * In some corner cases commands can be deleted from the hash
@@ -440,8 +451,13 @@ struct iscsi_cmnd {
 #define ISCSI_TM_DATA_WAIT_TIMEOUT     (10 * HZ)
 #define ISCSI_TM_DATA_WAIT_SCHED_TIMEOUT (ISCSI_TM_DATA_WAIT_TIMEOUT + HZ)
 
+#define ISCSI_CTR_OPEN_STATE_CLOSED    0
+#define ISCSI_CTR_OPEN_STATE_OPEN      1
+#define ISCSI_CTR_OPEN_STATE_CLOSING   2       
+
 extern struct mutex target_mgmt_mutex;
 
+extern int ctr_open_state;
 extern const struct file_operations ctr_fops;
 
 extern spinlock_t iscsi_rd_lock;
@@ -470,8 +486,8 @@ extern void iscsi_fail_data_waiting_cmnd(struct iscsi_cmnd *cmnd);
 /* conn.c */
 extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16);
 extern void conn_reinst_finished(struct iscsi_conn *);
-extern int conn_add(struct iscsi_session *, struct iscsi_kern_conn_info *);
-extern int conn_del(struct iscsi_session *, struct iscsi_kern_conn_info *);
+extern int __add_conn(struct iscsi_session *, struct iscsi_kern_conn_info *);
+extern int __del_conn(struct iscsi_session *, struct iscsi_kern_conn_info *);
 #ifdef CONFIG_SCST_PROC
 extern int conn_free(struct iscsi_conn *);
 #endif
@@ -499,51 +515,62 @@ extern void iscsi_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *scst_mcmd);
 extern void req_add_to_write_timeout_list(struct iscsi_cmnd *req);
 
 /* target.c */
-#ifndef CONFIG_SCST_PROC
+#ifdef CONFIG_SCST_PROC
+extern const struct seq_operations iscsi_seq_op;
+#else
 extern const struct attribute *iscsi_tgt_attrs[];
 extern ssize_t iscsi_enable_target(struct scst_tgt *scst_tgt, const char *buf,
        size_t size);
 extern bool iscsi_is_target_enabled(struct scst_tgt *scst_tgt);
+extern ssize_t iscsi_sysfs_send_event(uint32_t tid,
+       enum iscsi_kern_event_code code,
+       const char *param1, const char *param2, void **data);
 #endif
-struct iscsi_target *target_lookup_by_id(u32);
-extern int target_add(struct iscsi_kern_target_info *);
-extern int target_enable(struct iscsi_kern_target_info *);
-extern int target_disable(struct iscsi_kern_target_info *);
-extern int target_del(u32 id);
+extern struct iscsi_target *target_lookup_by_id(u32);
+extern int __add_target(struct iscsi_kern_target_info *);
+extern int __del_target(u32 id);
+extern ssize_t iscsi_sysfs_add_target(const char *target_name,
+       const char *params);
+extern ssize_t iscsi_sysfs_del_target(const char *target_name);
+extern ssize_t iscsi_sysfs_mgmt_cmd(const char *cmd);
 extern void target_del_session(struct iscsi_target *target,
        struct iscsi_session *session, int flags);
 extern void target_del_all_sess(struct iscsi_target *target, int flags);
 extern void target_del_all(void);
 
-extern const struct seq_operations iscsi_seq_op;
-
 /* config.c */
 #ifdef CONFIG_SCST_PROC
 extern int iscsi_procfs_init(void);
 extern void iscsi_procfs_exit(void);
 #else
 extern const struct attribute *iscsi_attrs[];
+extern int iscsi_add_attr(struct iscsi_target *target,
+       const struct iscsi_kern_attr *user_info);
+extern void __iscsi_del_attr(struct iscsi_target *target,
+       struct iscsi_attr *tgt_attr);
 #endif
 
 /* session.c */
-#ifdef CONFIG_SCST_PROC
-int print_digest_state(char *p, size_t size, unsigned long flags);
-#else
+#ifndef CONFIG_SCST_PROC
 extern const struct attribute *iscsi_sess_attrs[];
 #endif
 extern const struct file_operations session_seq_fops;
 extern struct iscsi_session *session_lookup(struct iscsi_target *, u64);
 extern void sess_reinst_finished(struct iscsi_session *);
-extern int session_add(struct iscsi_target *, struct iscsi_kern_session_info *);
-extern int session_del(struct iscsi_target *, u64);
+extern int __add_session(struct iscsi_target *,
+       struct iscsi_kern_session_info *);
+extern int __del_session(struct iscsi_target *, u64);
 extern int session_free(struct iscsi_session *session, bool del);
 
 /* params.c */
-extern int iscsi_param_set(struct iscsi_target *,
-       struct iscsi_kern_param_info *, int);
+extern const char *iscsi_get_digest_name(int val, char *res);
+extern const char *iscsi_get_bool_value(int val);
+extern int iscsi_params_set(struct iscsi_target *,
+       struct iscsi_kern_params_info *, int);
 
 /* event.c */
-extern int event_send(u32, u64, u32, enum iscsi_kern_event_code);
+extern int event_send(u32, u64, u32, u32, enum iscsi_kern_event_code,
+       const char *param1, const char *param2);
 extern int event_init(void);
 extern void event_exit(void);
 
index 998ab0c..6bb9a38 100644 (file)
@@ -382,7 +382,7 @@ static void close_conn(struct iscsi_conn *conn)
 
        TRACE_ENTRY();
 
-       TRACE_CONN_CLOSE("Closing connection %p (conn_ref_cnt=%d)", conn,
+       TRACE_MGMT_DBG("Closing connection %p (conn_ref_cnt=%d)", conn,
                atomic_read(&conn->conn_ref_cnt));
 
        iscsi_extracheck_is_rd_thread(conn);
@@ -550,7 +550,8 @@ static void close_conn(struct iscsi_conn *conn)
 
        TRACE_CONN_CLOSE("Notifying user space about closing connection %p",
                         conn);
-       event_send(target->tid, session->sid, conn->cid, E_CONN_CLOSE);
+       event_send(target->tid, session->sid, conn->cid, 0, E_CONN_CLOSE,
+               NULL, NULL);
 
 #ifdef CONFIG_SCST_PROC
        mutex_lock(&target->target_mutex);
index 782438d..ce08437 100644 (file)
 #include "iscsi.h"
 #include "digest.h"
 
-#define        CHECK_PARAM(info, iparam, word, min, max)                             \
-do {                                                                         \
-       if (!(info)->partial || ((info)->partial & 1 << key_##word))          \
-               if ((iparam)[key_##word] < (min) ||                           \
-                       (iparam)[key_##word] > (max)) {                       \
-                       PRINT_ERROR("%s: %u is out of range (%u %u)",         \
-                               #word, (iparam)[key_##word], (min), (max));   \
-                       if ((iparam)[key_##word] < (min))                     \
-                               (iparam)[key_##word] = (min);                 \
-                       else                                                  \
-                               (iparam)[key_##word] = (max);                 \
-               }                                                             \
+#define        CHECK_PARAM(info, iparams, word, min, max)                              \
+do {                                                                           \
+       if (!(info)->partial || ((info)->partial & 1 << key_##word)) {          \
+               TRACE_DBG("%s: %u", #word, (iparams)[key_##word]);              \
+               if ((iparams)[key_##word] < (min) ||                            \
+                       (iparams)[key_##word] > (max)) {                        \
+                       PRINT_ERROR("%s: %u is out of range (%u %u)",           \
+                               #word, (iparams)[key_##word], (min), (max));    \
+                       if ((iparams)[key_##word] < (min))                      \
+                               (iparams)[key_##word] = (min);                  \
+                       else                                                    \
+                               (iparams)[key_##word] = (max);                  \
+               }                                                               \
+       }                                                                       \
 } while (0)
 
-#define        SET_PARAM(param, info, iparam, word)                                  \
-({                                                                           \
-       int changed = 0;                                                      \
-       if (!(info)->partial || ((info)->partial & 1 << key_##word)) {        \
-               if ((param)->word != (iparam)[key_##word])                    \
-                       changed = 1;                                          \
-               (param)->word = (iparam)[key_##word];                         \
-       }                                                                     \
-       changed;                                                              \
+#define        SET_PARAM(params, info, iparams, word)                                  \
+({                                                                             \
+       int changed = 0;                                                        \
+       if (!(info)->partial || ((info)->partial & 1 << key_##word)) {          \
+               if ((params)->word != (iparams)[key_##word])                    \
+                       changed = 1;                                            \
+               (params)->word = (iparams)[key_##word];                         \
+               TRACE_DBG("%s set to %u", #word, (iparams)[key_##word]);        \
+       }                                                                       \
+       changed;                                                                \
 })
 
-#define        GET_PARAM(param, info, iparam, word)                                  \
-do {                                                                         \
-       (iparam)[key_##word] = (param)->word;                                 \
+#define        GET_PARAM(params, info, iparams, word)                                  \
+do {                                                                           \
+       (iparams)[key_##word] = (params)->word;                                 \
 } while (0)
 
-static const char *get_bool_name(int val)
+const char *iscsi_get_bool_value(int val)
 {
        if (val)
                return "Yes";
@@ -54,210 +57,234 @@ static const char *get_bool_name(int val)
                return "No";
 }
 
-static const char *get_digest_name(int val)
+const char *iscsi_get_digest_name(int val, char *res)
 {
-       if (val == DIGEST_NONE)
-               return "None";
-       else if (val == DIGEST_CRC32C)
-               return "CRC32C";
-       else
-               return "Unknown";
+       int pos = 0;
+
+       if (val & DIGEST_NONE)
+               pos = sprintf(&res[pos], "%s", "None");
+
+       if (val & DIGEST_CRC32C)
+               pos += sprintf(&res[pos], "%s%s", (pos != 0) ? ", " : "",
+                       "CRC32C");
+
+       if (pos == 0)
+               sprintf(&res[pos], "%s", "Unknown");
+
+       return res;
 }
 
-static void log_params(struct iscsi_sess_param *param)
+static void log_params(struct iscsi_sess_params *params)
 {
+       char digest_name[64];
+
        PRINT_INFO("Negotiated parameters: InitialR2T %s, ImmediateData %s, "
                "MaxConnections %d, MaxRecvDataSegmentLength %d, "
                "MaxXmitDataSegmentLength %d, ",
-               get_bool_name(param->initial_r2t),
-               get_bool_name(param->immediate_data), param->max_connections,
-               param->max_recv_data_length, param->max_xmit_data_length);
+               iscsi_get_bool_value(params->initial_r2t),
+               iscsi_get_bool_value(params->immediate_data), params->max_connections,
+               params->max_recv_data_length, params->max_xmit_data_length);
        PRINT_INFO("    MaxBurstLength %d, FirstBurstLength %d, "
                "DefaultTime2Wait %d, DefaultTime2Retain %d, ",
-               param->max_burst_length, param->first_burst_length,
-               param->default_wait_time, param->default_retain_time);
+               params->max_burst_length, params->first_burst_length,
+               params->default_wait_time, params->default_retain_time);
        PRINT_INFO("    MaxOutstandingR2T %d, DataPDUInOrder %s, "
                "DataSequenceInOrder %s, ErrorRecoveryLevel %d, ",
-               param->max_outstanding_r2t,
-               get_bool_name(param->data_pdu_inorder),
-               get_bool_name(param->data_sequence_inorder),
-               param->error_recovery_level);
+               params->max_outstanding_r2t,
+               iscsi_get_bool_value(params->data_pdu_inorder),
+               iscsi_get_bool_value(params->data_sequence_inorder),
+               params->error_recovery_level);
        PRINT_INFO("    HeaderDigest %s, DataDigest %s, OFMarker %s, "
                "IFMarker %s, OFMarkInt %d, IFMarkInt %d",
-               get_digest_name(param->header_digest),
-               get_digest_name(param->data_digest),
-               get_bool_name(param->ofmarker),
-               get_bool_name(param->ifmarker),
-               param->ofmarkint, param->ifmarkint);
+               iscsi_get_digest_name(params->header_digest, digest_name),
+               iscsi_get_digest_name(params->data_digest, digest_name),
+               iscsi_get_bool_value(params->ofmarker),
+               iscsi_get_bool_value(params->ifmarker),
+               params->ofmarkint, params->ifmarkint);
 }
 
 /* target_mutex supposed to be locked */
-static void sess_param_check(struct iscsi_kern_param_info *info)
+static void sess_params_check(struct iscsi_kern_params_info *info)
 {
-       int32_t *iparam = info->session_param;
-
-       CHECK_PARAM(info, iparam, max_connections, 1, 1);
-       CHECK_PARAM(info, iparam, max_recv_data_length, 512,
-                   (int32_t) (ISCSI_CONN_IOV_MAX * PAGE_SIZE));
-       CHECK_PARAM(info, iparam, max_xmit_data_length, 512,
-                   (int32_t) (ISCSI_CONN_IOV_MAX * PAGE_SIZE));
-       CHECK_PARAM(info, iparam, error_recovery_level, 0, 0);
-       CHECK_PARAM(info, iparam, data_pdu_inorder, 0, 1);
-       CHECK_PARAM(info, iparam, data_sequence_inorder, 0, 1);
-
-       digest_alg_available(&iparam[key_header_digest]);
-       digest_alg_available(&iparam[key_data_digest]);
-
-       CHECK_PARAM(info, iparam, ofmarker, 0, 0);
-       CHECK_PARAM(info, iparam, ifmarker, 0, 0);
+       int32_t *iparams = info->session_params;
+       const int max_len = ISCSI_CONN_IOV_MAX * PAGE_SIZE;
+
+       CHECK_PARAM(info, iparams, initial_r2t, 0, 1);
+       CHECK_PARAM(info, iparams, immediate_data, 0, 1);
+       CHECK_PARAM(info, iparams, max_connections, 1, 1);
+       CHECK_PARAM(info, iparams, max_recv_data_length, 512, max_len);
+       CHECK_PARAM(info, iparams, max_xmit_data_length, 512, max_len);
+       CHECK_PARAM(info, iparams, max_burst_length, 512, max_len);
+       CHECK_PARAM(info, iparams, first_burst_length, 512, max_len);
+       CHECK_PARAM(info, iparams, max_outstanding_r2t, 1, 65535);
+       CHECK_PARAM(info, iparams, error_recovery_level, 0, 0);
+       CHECK_PARAM(info, iparams, data_pdu_inorder, 0, 1);
+       CHECK_PARAM(info, iparams, data_sequence_inorder, 0, 1);
+
+       digest_alg_available(&iparams[key_header_digest]);
+       digest_alg_available(&iparams[key_data_digest]);
+
+       CHECK_PARAM(info, iparams, ofmarker, 0, 0);
+       CHECK_PARAM(info, iparams, ifmarker, 0, 0);
+
+       return;
 }
 
 /* target_mutex supposed to be locked */
-static void sess_param_set(struct iscsi_sess_param *param,
-                          struct iscsi_kern_param_info *info)
+static void sess_params_set(struct iscsi_sess_params *params,
+                          struct iscsi_kern_params_info *info)
 {
-       int32_t *iparam = info->session_param;
-
-       SET_PARAM(param, info, iparam, initial_r2t);
-       SET_PARAM(param, info, iparam, immediate_data);
-       SET_PARAM(param, info, iparam, max_connections);
-       SET_PARAM(param, info, iparam, max_recv_data_length);
-       SET_PARAM(param, info, iparam, max_xmit_data_length);
-       SET_PARAM(param, info, iparam, max_burst_length);
-       SET_PARAM(param, info, iparam, first_burst_length);
-       SET_PARAM(param, info, iparam, default_wait_time);
-       SET_PARAM(param, info, iparam, default_retain_time);
-       SET_PARAM(param, info, iparam, max_outstanding_r2t);
-       SET_PARAM(param, info, iparam, data_pdu_inorder);
-       SET_PARAM(param, info, iparam, data_sequence_inorder);
-       SET_PARAM(param, info, iparam, error_recovery_level);
-       SET_PARAM(param, info, iparam, header_digest);
-       SET_PARAM(param, info, iparam, data_digest);
-       SET_PARAM(param, info, iparam, ofmarker);
-       SET_PARAM(param, info, iparam, ifmarker);
-       SET_PARAM(param, info, iparam, ofmarkint);
-       SET_PARAM(param, info, iparam, ifmarkint);
+       int32_t *iparams = info->session_params;
+
+       SET_PARAM(params, info, iparams, initial_r2t);
+       SET_PARAM(params, info, iparams, immediate_data);
+       SET_PARAM(params, info, iparams, max_connections);
+       SET_PARAM(params, info, iparams, max_recv_data_length);
+       SET_PARAM(params, info, iparams, max_xmit_data_length);
+       SET_PARAM(params, info, iparams, max_burst_length);
+       SET_PARAM(params, info, iparams, first_burst_length);
+       SET_PARAM(params, info, iparams, default_wait_time);
+       SET_PARAM(params, info, iparams, default_retain_time);
+       SET_PARAM(params, info, iparams, max_outstanding_r2t);
+       SET_PARAM(params, info, iparams, data_pdu_inorder);
+       SET_PARAM(params, info, iparams, data_sequence_inorder);
+       SET_PARAM(params, info, iparams, error_recovery_level);
+       SET_PARAM(params, info, iparams, header_digest);
+       SET_PARAM(params, info, iparams, data_digest);
+       SET_PARAM(params, info, iparams, ofmarker);
+       SET_PARAM(params, info, iparams, ifmarker);
+       SET_PARAM(params, info, iparams, ofmarkint);
+       SET_PARAM(params, info, iparams, ifmarkint);
+       return;
 }
 
-static void sess_param_get(struct iscsi_sess_param *param,
-                          struct iscsi_kern_param_info *info)
+static void sess_params_get(struct iscsi_sess_params *params,
+                          struct iscsi_kern_params_info *info)
 {
-       int32_t *iparam = info->session_param;
-
-       GET_PARAM(param, info, iparam, initial_r2t);
-       GET_PARAM(param, info, iparam, immediate_data);
-       GET_PARAM(param, info, iparam, max_connections);
-       GET_PARAM(param, info, iparam, max_recv_data_length);
-       GET_PARAM(param, info, iparam, max_xmit_data_length);
-       GET_PARAM(param, info, iparam, max_burst_length);
-       GET_PARAM(param, info, iparam, first_burst_length);
-       GET_PARAM(param, info, iparam, default_wait_time);
-       GET_PARAM(param, info, iparam, default_retain_time);
-       GET_PARAM(param, info, iparam, max_outstanding_r2t);
-       GET_PARAM(param, info, iparam, data_pdu_inorder);
-       GET_PARAM(param, info, iparam, data_sequence_inorder);
-       GET_PARAM(param, info, iparam, error_recovery_level);
-       GET_PARAM(param, info, iparam, header_digest);
-       GET_PARAM(param, info, iparam, data_digest);
-       GET_PARAM(param, info, iparam, ofmarker);
-       GET_PARAM(param, info, iparam, ifmarker);
-       GET_PARAM(param, info, iparam, ofmarkint);
-       GET_PARAM(param, info, iparam, ifmarkint);
+       int32_t *iparams = info->session_params;
+
+       GET_PARAM(params, info, iparams, initial_r2t);
+       GET_PARAM(params, info, iparams, immediate_data);
+       GET_PARAM(params, info, iparams, max_connections);
+       GET_PARAM(params, info, iparams, max_recv_data_length);
+       GET_PARAM(params, info, iparams, max_xmit_data_length);
+       GET_PARAM(params, info, iparams, max_burst_length);
+       GET_PARAM(params, info, iparams, first_burst_length);
+       GET_PARAM(params, info, iparams, default_wait_time);
+       GET_PARAM(params, info, iparams, default_retain_time);
+       GET_PARAM(params, info, iparams, max_outstanding_r2t);
+       GET_PARAM(params, info, iparams, data_pdu_inorder);
+       GET_PARAM(params, info, iparams, data_sequence_inorder);
+       GET_PARAM(params, info, iparams, error_recovery_level);
+       GET_PARAM(params, info, iparams, header_digest);
+       GET_PARAM(params, info, iparams, data_digest);
+       GET_PARAM(params, info, iparams, ofmarker);
+       GET_PARAM(params, info, iparams, ifmarker);
+       GET_PARAM(params, info, iparams, ofmarkint);
+       GET_PARAM(params, info, iparams, ifmarkint);
+       return;
 }
 
 /* target_mutex supposed to be locked */
-static void trgt_param_check(struct iscsi_kern_param_info *info)
+static void tgt_params_check(struct iscsi_kern_params_info *info)
 {
-       int32_t *iparam = info->target_param;
+       int32_t *iparams = info->target_params;
 
-       CHECK_PARAM(info, iparam, queued_cmnds, MIN_NR_QUEUED_CMNDS,
+       CHECK_PARAM(info, iparams, queued_cmnds, MIN_NR_QUEUED_CMNDS,
                    MAX_NR_QUEUED_CMNDS);
+       return;
 }
 
 /* target_mutex supposed to be locked */
-static void trgt_param_set(struct iscsi_target *target,
-                          struct iscsi_kern_param_info *info)
+static void tgt_params_set(struct iscsi_tgt_params *params,
+                          struct iscsi_kern_params_info *info)
 {
-       struct iscsi_trgt_param *param = &target->trgt_param;
-       int32_t *iparam = info->target_param;
+       int32_t *iparams = info->target_params;
 
-       SET_PARAM(param, info, iparam, queued_cmnds);
+       SET_PARAM(params, info, iparams, queued_cmnds);
+       return;
 }
 
 /* target_mutex supposed to be locked */
-static void trgt_param_get(struct iscsi_trgt_param *param,
-                          struct iscsi_kern_param_info *info)
+static void tgt_params_get(struct iscsi_tgt_params *params,
+                          struct iscsi_kern_params_info *info)
 {
-       int32_t *iparam = info->target_param;
+       int32_t *iparams = info->target_params;
 
-       GET_PARAM(param, info, iparam, queued_cmnds);
+       GET_PARAM(params, info, iparams, queued_cmnds);
+       return;
 }
 
 /* target_mutex supposed to be locked */
-static int trgt_param(struct iscsi_target *target,
-                     struct iscsi_kern_param_info *info, int set)
+static int iscsi_tgt_params_set(struct iscsi_session *session,
+                     struct iscsi_kern_params_info *info, int set)
 {
-       if (set) {
-               struct iscsi_trgt_param *prm;
-               trgt_param_check(info);
-               trgt_param_set(target, info);
+       struct iscsi_tgt_params *params = &session->tgt_params;
 
-               prm = &target->trgt_param;
-               PRINT_INFO("Target parameter changed: QueuedCommands %d",
-                       prm->queued_cmnds);
+       if (set) {
+               tgt_params_check(info);
+               tgt_params_set(params, info);
+               PRINT_INFO("Target parameters set for session %llx: "
+                       "QueuedCommands %d", session->sid,
+                       params->queued_cmnds);
        } else
-               trgt_param_get(&target->trgt_param, info);
+               tgt_params_get(params, info);
 
        return 0;
 }
 
 /* target_mutex supposed to be locked */
-static int sess_param(struct iscsi_target *target,
-                     struct iscsi_kern_param_info *info, int set)
+static int iscsi_sess_params_set(struct iscsi_session *session,
+       struct iscsi_kern_params_info *info, int set)
 {
-       struct iscsi_session *session = NULL;
-       struct iscsi_sess_param *param;
-       int err = -ENOENT;
+       struct iscsi_sess_params *params;
 
        if (set)
-               sess_param_check(info);
-
-       if (info->sid) {
-               session = session_lookup(target, info->sid);
-               if (!session)
-                       goto out;
-               if (set && !list_empty(&session->conn_list)) {
-                       err = -EBUSY;
-                       goto out;
-               }
-               param = &session->sess_param;
-       } else
-               param = &target->trgt_sess_param;
+               sess_params_check(info);
+
+       params = &session->sess_params;
 
        if (set) {
-               sess_param_set(param, info);
-               if (session != NULL)
-                       log_params(param);
+               sess_params_set(params, info);
+               log_params(params);
        } else
-               sess_param_get(param, info);
+               sess_params_get(params, info);
 
-       err = 0;
-out:
-       return err;
+       return 0;
 }
 
 /* target_mutex supposed to be locked */
-int iscsi_param_set(struct iscsi_target *target,
-       struct iscsi_kern_param_info *info, int set)
+int iscsi_params_set(struct iscsi_target *target,
+       struct iscsi_kern_params_info *info, int set)
 {
        int err;
+       struct iscsi_session *session;
 
-       if (info->param_type == key_session)
-               err = sess_param(target, info, set);
-       else if (info->param_type == key_target)
-               err = trgt_param(target, info, set);
+       if (info->sid == 0) {
+               PRINT_ERROR("sid must not be %d", 0);
+               err = -EINVAL;
+               goto out;
+       }
+
+       session = session_lookup(target, info->sid);
+       if (session == NULL) {
+               PRINT_ERROR("Session for sid %llx not found", info->sid);
+               err = -ENOENT;
+               goto out;
+       }
+
+       if (set && !list_empty(&session->conn_list)) {
+               err = -EBUSY;
+               goto out;
+       }
+
+       if (info->params_type == key_session)
+               err = iscsi_sess_params_set(session, info, set);
+       else if (info->params_type == key_target)
+               err = iscsi_tgt_params_set(session, info, set);
        else
                err = -EINVAL;
 
+out:
        return err;
 }
index 49c291c..9d26c7c 100644 (file)
 
 #include "iscsi.h"
 
-#ifdef CONFIG_SCST_PROC
-int print_digest_state(char *p, size_t size, unsigned long flags)
-#else
-static int print_digest_state(char *p, size_t size, unsigned long flags)
-#endif
-{
-       int pos;
-
-       if (DIGEST_NONE & flags)
-               pos = scnprintf(p, size, "%s", "none");
-       else if (DIGEST_CRC32C & flags)
-               pos = scnprintf(p, size, "%s", "crc32c");
-       else
-               pos = scnprintf(p, size, "%s", "unknown");
-
-       return pos;
-}
-
 /* target_mutex supposed to be locked */
 struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid)
 {
@@ -62,10 +44,7 @@ static int iscsi_session_alloc(struct iscsi_target *target,
 
        session->target = target;
        session->sid = info->sid;
-       session->sess_param = target->trgt_sess_param;
-       session->max_queued_cmnds = target->trgt_param.queued_cmnds;
        atomic_set(&session->active_cmds, 0);
-
        session->exp_cmd_sn = info->exp_cmd_sn;
 
        session->initiator_name = kstrdup(info->initiator_name, GFP_KERNEL);
@@ -74,6 +53,7 @@ static int iscsi_session_alloc(struct iscsi_target *target,
                goto err;
        }
 
+#ifdef CONFIG_SCST_PROC
        name = kmalloc(strlen(info->user_name) + strlen(info->initiator_name) +
                        1, GFP_KERNEL);
        if (name == NULL) {
@@ -85,6 +65,9 @@ static int iscsi_session_alloc(struct iscsi_target *target,
                sprintf(name, "%s@%s", info->user_name, info->initiator_name);
        else
                sprintf(name, "%s", info->initiator_name);
+#else
+       name =  info->initiator_name;
+#endif
 
        INIT_LIST_HEAD(&session->conn_list);
        INIT_LIST_HEAD(&session->pending_list);
@@ -105,7 +88,9 @@ static int iscsi_session_alloc(struct iscsi_target *target,
                goto err;
        }
 
+#ifdef CONFIG_SCST_PROC
        kfree(name);
+#endif
 
        scst_sess_set_tgt_priv(session->scst_sess, session);
 
@@ -119,7 +104,9 @@ err:
        if (session) {
                kfree(session->initiator_name);
                kfree(session);
+#ifdef CONFIG_SCST_PROC
                kfree(name);
+#endif
        }
        return err;
 }
@@ -145,13 +132,14 @@ void sess_reinst_finished(struct iscsi_session *sess)
 }
 
 /* target_mgmt_mutex supposed to be locked */
-int session_add(struct iscsi_target *target,
+int __add_session(struct iscsi_target *target,
        struct iscsi_kern_session_info *info)
 {
        struct iscsi_session *new_sess = NULL, *sess, *old_sess;
-       int err = 0;
+       int err = 0, i;
        union iscsi_sid sid;
        bool reinstatement = false;
+       struct iscsi_kern_params_info *params_info;
 
        TRACE_MGMT_DBG("Adding session SID %llx", info->sid);
 
@@ -169,6 +157,14 @@ int session_add(struct iscsi_target *target,
                goto out_err_unlock;
        }
 
+       params_info = kmalloc(sizeof(*params_info), GFP_KERNEL);
+       if (params_info == NULL) {
+               PRINT_ERROR("Unable to allocate params info (size %d)",
+                       sizeof(*params_info));
+               err = -ENOMEM;
+               goto out_err_unlock;
+       }
+
        sid = *(union iscsi_sid *)&info->sid;
        sid.id.tsih = 0;
        old_sess = NULL;
@@ -194,6 +190,31 @@ int session_add(struct iscsi_target *target,
 
        list_add_tail(&new_sess->session_list_entry, &target->session_list);
 
+       memset(params_info, 0, sizeof(*params_info));
+       params_info->tid = target->tid;
+       params_info->sid = info->sid;
+       params_info->params_type = key_session;
+       for (i = 0; i < session_key_last; i++)
+               params_info->session_params[i] = info->session_params[i];
+
+       err = iscsi_params_set(target, params_info, 1);
+       if (err != 0)
+               goto out_del;
+
+       memset(params_info, 0, sizeof(*params_info));
+       params_info->tid = target->tid;
+       params_info->sid = info->sid;
+       params_info->params_type = key_target;
+       for (i = 0; i < target_key_last; i++)
+               params_info->target_params[i] = info->target_params[i];
+
+       err = iscsi_params_set(target, params_info, 1);
+       if (err != 0)
+               goto out_del;
+
+       kfree(params_info);
+       params_info = NULL;
+
        if (old_sess != NULL) {
                reinstatement = true;
 
@@ -226,6 +247,10 @@ int session_add(struct iscsi_target *target,
 out:
        return err;
 
+out_del:
+       list_del(&new_sess->session_list_entry);
+       kfree(params_info);
+
 out_err_unlock:
        mutex_unlock(&target->target_mutex);
 
@@ -312,7 +337,7 @@ int session_free(struct iscsi_session *session, bool del)
 }
 
 /* target_mutex supposed to be locked */
-int session_del(struct iscsi_target *target, u64 sid)
+int __del_session(struct iscsi_target *target, u64 sid)
 {
        struct iscsi_session *session;
 
@@ -368,6 +393,76 @@ const struct file_operations session_seq_fops = {
 
 #else /* CONFIG_SCST_PROC */
 
+#define ISCSI_SESS_BOOL_PARAM_ATTR(name, exported_name)                                \
+static ssize_t iscsi_sess_show_##name(struct kobject *kobj,                    \
+       struct kobj_attribute *attr, char *buf)                                 \
+{                                                                              \
+       int pos;                                                                \
+       struct scst_session *scst_sess;                                         \
+       struct iscsi_session *sess;                                             \
+                                                                               \
+       scst_sess = container_of(kobj, struct scst_session, sess_kobj);         \
+       sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);       \
+                                                                               \
+       pos = sprintf(buf, "%s\n",                                              \
+               iscsi_get_bool_value(sess->sess_params.name));                  \
+                                                                               \
+       return pos;                                                             \
+}                                                                              \
+                                                                               \
+static struct kobj_attribute iscsi_sess_attr_##name =                          \
+       __ATTR(exported_name, S_IRUGO, iscsi_sess_show_##name, NULL);
+
+#define ISCSI_SESS_INT_PARAM_ATTR(name, exported_name)                         \
+static ssize_t iscsi_sess_show_##name(struct kobject *kobj,                    \
+       struct kobj_attribute *attr, char *buf)                                 \
+{                                                                              \
+       int pos;                                                                \
+       struct scst_session *scst_sess;                                         \
+       struct iscsi_session *sess;                                             \
+                                                                               \
+       scst_sess = container_of(kobj, struct scst_session, sess_kobj);         \
+       sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);       \
+                                                                               \
+       pos = sprintf(buf, "%d\n", sess->sess_params.name);                     \
+                                                                               \
+       return pos;                                                             \
+}                                                                              \
+                                                                               \
+static struct kobj_attribute iscsi_sess_attr_##name =                          \
+       __ATTR(exported_name, S_IRUGO, iscsi_sess_show_##name, NULL);
+
+#define ISCSI_SESS_DIGEST_PARAM_ATTR(name, exported_name)                      \
+static ssize_t iscsi_sess_show_##name(struct kobject *kobj,                    \
+       struct kobj_attribute *attr, char *buf)                                 \
+{                                                                              \
+       int pos;                                                                \
+       struct scst_session *scst_sess;                                         \
+       struct iscsi_session *sess;                                             \
+       char digest_name[64];                                                   \
+                                                                               \
+       scst_sess = container_of(kobj, struct scst_session, sess_kobj);         \
+       sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);       \
+                                                                               \
+       pos = sprintf(buf, "%s\n", iscsi_get_digest_name(                       \
+                       sess->sess_params.name, digest_name));                  \
+                                                                               \
+       return pos;                                                             \
+}                                                                              \
+                                                                               \
+static struct kobj_attribute iscsi_sess_attr_##name =                          \
+       __ATTR(exported_name, S_IRUGO, iscsi_sess_show_##name, NULL);
+
+ISCSI_SESS_BOOL_PARAM_ATTR(initial_r2t, InitialR2T);
+ISCSI_SESS_BOOL_PARAM_ATTR(immediate_data, ImmediateData);
+ISCSI_SESS_INT_PARAM_ATTR(max_recv_data_length, MaxRecvDataSegmentLength);
+ISCSI_SESS_INT_PARAM_ATTR(max_xmit_data_length, MaxXmitDataSegmentLength);
+ISCSI_SESS_INT_PARAM_ATTR(max_burst_length, MaxBurstLength);
+ISCSI_SESS_INT_PARAM_ATTR(first_burst_length, FirstBurstLength);
+ISCSI_SESS_INT_PARAM_ATTR(max_outstanding_r2t, MaxOutstandingR2T);
+ISCSI_SESS_DIGEST_PARAM_ATTR(header_digest, HeaderDigest);
+ISCSI_SESS_DIGEST_PARAM_ATTR(data_digest, DataDigest);
+
 static ssize_t iscsi_sess_sid_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf)
 {
@@ -386,7 +481,7 @@ static ssize_t iscsi_sess_sid_show(struct kobject *kobj,
        return pos;
 }
 
-static struct kobj_attribute iscsi_sess_sid_attr =
+static struct kobj_attribute iscsi_attr_sess_sid =
        __ATTR(sid, S_IRUGO, iscsi_sess_sid_show, NULL);
 
 static ssize_t iscsi_sess_reinstating_show(struct kobject *kobj,
@@ -407,58 +502,60 @@ static ssize_t iscsi_sess_reinstating_show(struct kobject *kobj,
        return pos;
 }
 
-static struct kobj_attribute iscsi_sess_reinstating_attr =
+static struct kobj_attribute iscsi_sess_attr_reinstating =
        __ATTR(reinstating, S_IRUGO, iscsi_sess_reinstating_show, NULL);
 
-static ssize_t iscsi_sess_hdigest_show(struct kobject *kobj,
-       struct kobj_attribute *attr, char *buf)
+static ssize_t iscsi_sess_force_close_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
 {
-       int pos;
+       int res;
        struct scst_session *scst_sess;
        struct iscsi_session *sess;
+       struct iscsi_conn *conn;
 
        TRACE_ENTRY();
 
        scst_sess = container_of(kobj, struct scst_session, sess_kobj);
        sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);
 
-       pos = print_digest_state(buf, SCST_SYSFS_BLOCK_SIZE,
-               sess->sess_param.header_digest);
-
-       TRACE_EXIT_RES(pos);
-       return pos;
-}
+       if (mutex_lock_interruptible(&sess->target->target_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
 
-static struct kobj_attribute iscsi_sess_hdigest_attr =
-       __ATTR(hdigest, S_IRUGO, iscsi_sess_hdigest_show, NULL);
+       PRINT_INFO("Deleting session %llu with initiator %s (%p)",
+               (long long unsigned int)sess->sid, sess->initiator_name, sess);
 
-static ssize_t iscsi_sess_ddigest_show(struct kobject *kobj,
-       struct kobj_attribute *attr, char *buf)
-{
-       int pos;
-       struct scst_session *scst_sess;
-       struct iscsi_session *sess;
+       list_for_each_entry(conn, &sess->conn_list, conn_list_entry) {
+               TRACE_MGMT_DBG("Deleting connection with initiator %p", conn);
+               __mark_conn_closed(conn, ISCSI_CONN_ACTIVE_CLOSE|ISCSI_CONN_DELETING);
+       }
 
-       TRACE_ENTRY();
+       mutex_unlock(&sess->target->target_mutex);
 
-       scst_sess = container_of(kobj, struct scst_session, sess_kobj);
-       sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);
-
-       pos = print_digest_state(buf, SCST_SYSFS_BLOCK_SIZE,
-               sess->sess_param.data_digest);
+       res = count;
 
-       TRACE_EXIT_RES(pos);
-       return pos;
+out:
+       TRACE_EXIT_RES(res);
+       return res;
 }
 
-static struct kobj_attribute iscsi_sess_ddigest_attr =
-       __ATTR(ddigest, S_IRUGO, iscsi_sess_ddigest_show, NULL);
+static struct kobj_attribute iscsi_sess_attr_force_close =
+       __ATTR(force_close, S_IWUSR, NULL, iscsi_sess_force_close_store);
 
 const struct attribute *iscsi_sess_attrs[] = {
-       &iscsi_sess_sid_attr.attr,
-       &iscsi_sess_reinstating_attr.attr,
-       &iscsi_sess_hdigest_attr.attr,
-       &iscsi_sess_ddigest_attr.attr,
+       &iscsi_sess_attr_initial_r2t.attr,
+       &iscsi_sess_attr_immediate_data.attr,
+       &iscsi_sess_attr_max_recv_data_length.attr,
+       &iscsi_sess_attr_max_xmit_data_length.attr,
+       &iscsi_sess_attr_max_burst_length.attr,
+       &iscsi_sess_attr_first_burst_length.attr,
+       &iscsi_sess_attr_max_outstanding_r2t.attr,
+       &iscsi_sess_attr_header_digest.attr,
+       &iscsi_sess_attr_data_digest.attr,
+       &iscsi_attr_sess_sid.attr,
+       &iscsi_sess_attr_reinstating.attr,
+       &iscsi_sess_attr_force_close.attr,
        NULL,
 };
 
index 7f56b13..b8043d8 100644 (file)
@@ -20,7 +20,6 @@
 #include "digest.h"
 
 #define MAX_NR_TARGETS         (1UL << 30)
-#define SYSFS_WAIT_TIMEOUT     (15 * HZ)
 
 DEFINE_MUTEX(target_mgmt_mutex);
 
@@ -42,7 +41,7 @@ struct iscsi_target *target_lookup_by_id(u32 id)
 }
 
 /* target_mgmt_mutex supposed to be locked */
-static struct iscsi_target *target_lookup_by_name(char *name)
+static struct iscsi_target *target_lookup_by_name(const char *name)
 {
        struct iscsi_target *target;
 
@@ -54,7 +53,8 @@ static struct iscsi_target *target_lookup_by_name(char *name)
 }
 
 /* target_mgmt_mutex supposed to be locked */
-static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid)
+static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid,
+       struct iscsi_target **out_target)
 {
        int err = -EINVAL, len;
        char *name = info->name;
@@ -81,11 +81,13 @@ static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid)
 
        target->tid = info->tid = tid;
 
-       strncpy(target->name, name, sizeof(target->name) - 1);
+       strlcpy(target->name, name, sizeof(target->name));
 
        mutex_init(&target->target_mutex);
-       mutex_init(&target->target_sysfs_mutex);
        INIT_LIST_HEAD(&target->session_list);
+#ifndef CONFIG_SCST_PROC
+       INIT_LIST_HEAD(&target->attrs_list);
+#endif
 
        target->scst_tgt = scst_register(&iscsi_template, target->name);
        if (!target->scst_tgt) {
@@ -98,6 +100,8 @@ static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid)
 
        list_add_tail(&target->target_list_entry, &target_list);
 
+       *out_target = target;
+
        return 0;
 
 out_free:
@@ -111,23 +115,51 @@ out:
 }
 
 /* target_mgmt_mutex supposed to be locked */
-int target_add(struct iscsi_kern_target_info *info)
+int __add_target(struct iscsi_kern_target_info *info)
 {
-       int err = -EEXIST;
+       int err;
        u32 tid = info->tid;
+       struct iscsi_target *target;
+       struct iscsi_kern_params_info *params_info;
+       struct iscsi_kern_attr *attr_info;
+       union add_info_union {
+               struct iscsi_kern_params_info params_info;
+               struct iscsi_kern_attr attr_info;
+       } *add_info;
+#ifndef CONFIG_SCST_PROC
+       int i, rc;
+       unsigned long attrs_ptr_long;
+       struct iscsi_kern_attr __user *attrs_ptr;
+#endif
 
        if (nr_targets > MAX_NR_TARGETS) {
                err = -EBUSY;
                goto out;
        }
 
-       if (target_lookup_by_name(info->name))
+       if (target_lookup_by_name(info->name)) {
+               PRINT_ERROR("Target %s already exist!", info->name);
+               err = -EEXIST;
                goto out;
+       }
 
-       if (tid && target_lookup_by_id(tid))
+       if (tid && target_lookup_by_id(tid)) {
+               PRINT_ERROR("Target %u already exist!", tid);
+               err = -EEXIST;
+               goto out;
+       }
+
+       add_info = kmalloc(sizeof(*add_info), GFP_KERNEL);
+       if (add_info == NULL) {
+               PRINT_ERROR("Unable to allocate additional info (size %d)",
+                       sizeof(*add_info));
+               err = -ENOMEM;
                goto out;
+       }
+       params_info = (struct iscsi_kern_params_info *)add_info;
+       attr_info = (struct iscsi_kern_attr *)add_info;
 
-       if (!tid) {
+       if (tid == 0) {
                do {
                        if (!++next_target_id)
                                ++next_target_id;
@@ -136,116 +168,81 @@ int target_add(struct iscsi_kern_target_info *info)
                tid = next_target_id;
        }
 
-       err = iscsi_target_create(info, tid);
-       if (!err)
-               nr_targets++;
-out:
-       return err;
-}
-
-static void target_destroy(struct iscsi_target *target)
-{
-       TRACE_MGMT_DBG("Destroying target tid %u", target->tid);
-
-       scst_unregister(target->scst_tgt);
-
-       kfree(target);
+       err = iscsi_target_create(info, tid, &target);
+       if (err != 0)
+               goto out_free;
 
-       module_put(THIS_MODULE);
-}
+       nr_targets++;
 
-/* target_mgmt_mutex supposed to be locked */
-int target_enable(struct iscsi_kern_target_info *info)
-{
-       int res = 0;
-       struct iscsi_target *tgt;
+#ifndef CONFIG_SCST_PROC
+       mutex_lock(&target->target_mutex);
 
-       TRACE_ENTRY();
+       attrs_ptr_long = info->attrs_ptr;
+       attrs_ptr = (struct iscsi_kern_attr __user *)attrs_ptr_long;
+       for (i = 0; i < info->attrs_num; i++) {
+               memset(attr_info, 0, sizeof(*attr_info));
+
+               rc = copy_from_user(attr_info, attrs_ptr, sizeof(*attr_info));
+               if (rc != 0) {
+                       PRINT_ERROR("copy_from_user() of users of target %s "
+                               "failed", info->name);
+                       err = -EFAULT;
+                       goto out_del_unlock;
+               }
 
-       tgt = target_lookup_by_id(info->tid);
-       if (tgt == NULL) {
-               PRINT_ERROR("Target %d not found", info->tid);
-               res = -EINVAL;
-               goto out;
-       }
+               attr_info->name[sizeof(attr_info->name)-1] = '\0';
 
-       mutex_lock(&tgt->target_sysfs_mutex);
+               err = iscsi_add_attr(target, attr_info);
+               if (err != 0)
+                       goto out_del_unlock;
 
-       if (tgt->expected_ioctl != ENABLE_TARGET) {
-               PRINT_ERROR("Unexpected ENABLE_TARGET IOCTL for target %d",
-                       tgt->tid);
-               res = -EINVAL;
-               goto out_unlock;
+               attrs_ptr++;
        }
 
-       tgt->expected_ioctl = 0;
-
-       WARN_ON(tgt->tgt_enabled);
-       tgt->tgt_enabled = 1;
-
-       tgt->ioctl_res = 0;
+       mutex_unlock(&target->target_mutex);
+#endif
 
-       complete_all(tgt->target_enabling_cmpl);
+       err = tid;
 
-out_unlock:
-       mutex_unlock(&tgt->target_sysfs_mutex);
+out_free:
+       kfree(add_info);
 
 out:
-       TRACE_EXIT_RES(res);
-       return res;
+       return err;
+
+#ifndef CONFIG_SCST_PROC
+out_del_unlock:
+       mutex_unlock(&target->target_mutex);
+       __del_target(tid);
+       goto out_free;
+#endif
 }
 
-/* target_mgmt_mutex supposed to be locked */
-int target_disable(struct iscsi_kern_target_info *info)
+static void target_destroy(struct iscsi_target *target)
 {
-       int res = 0;
-       struct iscsi_target *tgt;
-
-       TRACE_ENTRY();
-
-       tgt = target_lookup_by_id(info->tid);
-       if (tgt == NULL) {
-               PRINT_ERROR("Target %d not found", info->tid);
-               res = -EINVAL;
-               goto out;
-       }
+#ifndef CONFIG_SCST_PROC
+       struct iscsi_attr *attr, *t;
+#endif
 
-       mutex_lock(&tgt->target_sysfs_mutex);
+       TRACE_MGMT_DBG("Destroying target tid %u", target->tid);
 
-       if (tgt->expected_ioctl != DISABLE_TARGET) {
-               PRINT_ERROR("Unexpected DISABLE_TARGET IOCTL for target %d",
-                       tgt->tid);
-               res = -EINVAL;
-               goto out_unlock;
+#ifndef CONFIG_SCST_PROC
+       list_for_each_entry_safe(attr, t, &target->attrs_list,
+                               attrs_list_entry) {
+               __iscsi_del_attr(target, attr);
        }
+#endif
 
-       mutex_unlock(&tgt->target_sysfs_mutex);
-
-       mutex_lock(&tgt->target_mutex);
-       target_del_all_sess(tgt, ISCSI_CONN_ACTIVE_CLOSE | ISCSI_CONN_DELETING);
-       mutex_unlock(&tgt->target_mutex);
-
-       mutex_lock(&tgt->target_sysfs_mutex);
-
-       tgt->expected_ioctl = 0;
-
-       WARN_ON(!tgt->tgt_enabled);
-       tgt->tgt_enabled = 0;
-
-       tgt->ioctl_res = 0;
-
-       complete_all(tgt->target_enabling_cmpl);
+       scst_unregister(target->scst_tgt);
 
-out_unlock:
-       mutex_unlock(&tgt->target_sysfs_mutex);
+       kfree(target);
 
-out:
-       TRACE_EXIT_RES(res);
-       return res;
+       module_put(THIS_MODULE);
+       return;
 }
 
 /* target_mgmt_mutex supposed to be locked */
-int target_del(u32 id)
+int __del_target(u32 id)
 {
        struct iscsi_target *target;
        int err;
@@ -296,7 +293,7 @@ void target_del_session(struct iscsi_target *target,
        } else {
                TRACE_MGMT_DBG("Freeing session %p without connections",
                               session);
-               session_del(target, session->sid);
+               __del_session(target, session->sid);
        }
 
        TRACE_EXIT();
@@ -454,117 +451,161 @@ static ssize_t iscsi_tgt_tid_show(struct kobject *kobj,
        return pos;
 }
 
-static struct kobj_attribute iscsi_tgt_tid_attr =
+static struct kobj_attribute iscsi_tgt_attr_tid =
        __ATTR(tid, S_IRUGO, iscsi_tgt_tid_show, NULL);
 
 const struct attribute *iscsi_tgt_attrs[] = {
-       &iscsi_tgt_tid_attr.attr,
+       &iscsi_tgt_attr_tid.attr,
        NULL,
 };
 
+ssize_t iscsi_sysfs_send_event(uint32_t tid, enum iscsi_kern_event_code code,
+       const char *param1, const char *param2, void **data)
+{
+       int res;
+       struct scst_sysfs_user_info *info;
+
+       TRACE_ENTRY();
+
+       if (ctr_open_state != ISCSI_CTR_OPEN_STATE_OPEN) {
+               PRINT_ERROR("%s", "User space process not connected");
+               res = -EPERM;
+               goto out;
+       }
+
+       res = scst_sysfs_user_add_info(&info);
+       if (res != 0)
+               goto out;
+
+       TRACE_DBG("Sending event %d (tid %d, param1 %s, param2 %s, cookie %d, "
+               "info %p)", tid, code, param1, param2, info->info_cookie, info);
+
+       res = event_send(tid, 0, 0, info->info_cookie, code, param1, param2);
+       if (res <= 0) {
+               PRINT_ERROR("event_send() failed: %d", res);
+               goto out_free;
+       }
+
+       /*
+        * It may wait 30 secs in blocking connect to an unreacheable
+        * iSNS server. It must be fixed, but not now. ToDo.
+        */
+       res = scst_wait_info_completion(info, 31 * HZ);
+
+       if (data != NULL)
+               *data = info->data;
+
+out_free:
+       scst_sysfs_user_del_info(info);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
 ssize_t iscsi_enable_target(struct scst_tgt *scst_tgt, const char *buf,
        size_t size)
 {
        struct iscsi_target *tgt =
                (struct iscsi_target *)scst_tgt_get_tgt_priv(scst_tgt);
-       DECLARE_COMPLETION_ONSTACK(enabling_cmpl);
-       int res = 0, rc, ioctl_res = 0;
+       int res;
        bool enable;
+       uint32_t type;
 
        TRACE_ENTRY();
 
-       mutex_lock(&tgt->target_sysfs_mutex);
-
-       if (tgt->target_enabling_cmpl != NULL) {
-               TRACE_DBG("A sysfs command is being processed for target %d",
-                       tgt->tid);
-               res = -ETXTBSY;
-               goto out_unlock;
-       }
-
-       tgt->target_enabling_cmpl = &enabling_cmpl;
-
        switch (buf[0]) {
        case '0':
-               if (!tgt->tgt_enabled) {
-                       TRACE_DBG("Target %d already disabled", tgt->tid);
-                       goto out_null_unlock;
-               }
-               enable = true;
-               tgt->expected_ioctl = DISABLE_TARGET;
-               res = event_send(tgt->tid, 0, 0, E_DISABLE_TARGET);
-               if (res <= 0) {
-                       PRINT_ERROR("event_send() failed: %d", res);
-                       goto out_null_unlock;
-               }
+               type = E_DISABLE_TARGET;
+               enable = false;
                break;
        case '1':
-               if (tgt->tgt_enabled) {
-                       TRACE_DBG("Target %d already enabled", tgt->tid);
-                       goto out_null_unlock;
-               }
-               enable = false;
-               tgt->expected_ioctl = ENABLE_TARGET;
-               res = event_send(tgt->tid, 0, 0, E_ENABLE_TARGET);
-               if (res <= 0) {
-                       PRINT_ERROR("event_send() failed: %d", res);
-                       goto out_null_unlock;
-               }
+               type = E_ENABLE_TARGET;
+               enable = true;
                break;
        default:
                PRINT_ERROR("%s: Requested action not understood: %s",
                       __func__, buf);
                res = -EINVAL;
-               goto out_null_unlock;
+               goto out;
        }
 
-       mutex_unlock(&tgt->target_sysfs_mutex);
-
-       TRACE_DBG("Waiting for completion of enable/disable (%d) "
-               "target %d", enable, tgt->tid);
-
-       rc = wait_for_completion_interruptible_timeout(&enabling_cmpl,
-                                       SYSFS_WAIT_TIMEOUT);
-       if (res == 0) {
-               PRINT_ERROR("Timeout attempting to %s target %d",
-                       enable ? "enable" : "disable", tgt->tid);
-               res = -EBUSY;
-               /* go through */
-       } else if (res < 0) {
-               if (res != -ERESTARTSYS)
-                       PRINT_ERROR("wait_for_completion() failed: %d", res);
-               /* go through */
-       }
+       TRACE_DBG("%s target %d", enable ? "Enabling" : "Disabling", tgt->tid);
+
+       res = iscsi_sysfs_send_event(tgt->tid, type, NULL, NULL, NULL);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
 
-       TRACE_DBG("Waiting for completion of enable/disable (%d) "
-               "target %d finished with res %d", enable, tgt->tid, res);
+bool iscsi_is_target_enabled(struct scst_tgt *scst_tgt)
+{
+       struct iscsi_target *tgt =
+               (struct iscsi_target *)scst_tgt_get_tgt_priv(scst_tgt);
 
-       mutex_lock(&tgt->target_sysfs_mutex);
+       return tgt->tgt_enabled;
+}
 
-       ioctl_res = tgt->ioctl_res;
+ssize_t iscsi_sysfs_add_target(const char *target_name, const char *params)
+{
+       int res;
 
-out_null_unlock:
-       tgt->target_enabling_cmpl = NULL;
+       TRACE_ENTRY();
 
-out_unlock:
-       mutex_unlock(&tgt->target_sysfs_mutex);
+       res = iscsi_sysfs_send_event(0, E_ADD_TARGET, target_name,
+                       params, NULL);
+       if (res > 0) {
+               /* It's tid */
+               res = 0;
+       }
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+ssize_t iscsi_sysfs_del_target(const char *target_name)
+{
+       int res = 0, tid;
+
+       TRACE_ENTRY();
 
-       if (res == 0) {
-               res = ioctl_res;
-               if (res == 0)
-                       res = size;
+       /* We don't want to have tgt visible after the mutex unlock */
+       {
+               struct iscsi_target *tgt;
+               mutex_lock(&target_mgmt_mutex);
+               tgt = target_lookup_by_name(target_name);
+               if (tgt == NULL) {
+                       PRINT_ERROR("Target %s not found", target_name);
+                       mutex_unlock(&target_mgmt_mutex);
+                       res = -ENOENT;
+                       goto out;
+               }
+               tid = tgt->tid;
+               mutex_unlock(&target_mgmt_mutex);
        }
 
+       TRACE_DBG("Deleting target %s (tid %d)", target_name, tid);
+
+       res = iscsi_sysfs_send_event(tid, E_DEL_TARGET, NULL, NULL, NULL);
+
+out:
        TRACE_EXIT_RES(res);
        return res;
 }
 
-bool iscsi_is_target_enabled(struct scst_tgt *scst_tgt)
+ssize_t iscsi_sysfs_mgmt_cmd(const char *cmd)
 {
-       struct iscsi_target *tgt =
-               (struct iscsi_target *)scst_tgt_get_tgt_priv(scst_tgt);
+       int res;
 
-       return tgt->tgt_enabled;
+       TRACE_ENTRY();
+
+       TRACE_DBG("Sending mgmt cmd %s", cmd);
+
+       res = iscsi_sysfs_send_event(0, E_MGMT_CMD, cmd, NULL, NULL);
+
+       TRACE_EXIT_RES(res);
+       return res;
 }
 
 #endif /* CONFIG_SCST_PROC */
index 451f960..1b05619 100644 (file)
@@ -21,9 +21,12 @@ OBJS_D = $(SRCS_D:.c=.o)
 SRCS_ADM = iscsi_adm.c param.c
 OBJS_ADM = $(SRCS_ADM:.c=.o)
 
+SCST_INC_DIR := ../../scst/include
+#SCST_INC_DIR := /usr/local/include/scst
+
 CFLAGS += -O2 -fno-inline -Wall -Wextra -Wstrict-prototypes -Wno-sign-compare \
        -Wimplicit-function-declaration -Wno-unused-parameter \
-       -Wno-missing-field-initializers -g -I../include
+       -Wno-missing-field-initializers -g -I../include -I$(SCST_INC_DIR)
 CFLAGS += -D_GNU_SOURCE # required for glibc >= 2.8
 
 PROGRAMS = iscsi-scstd iscsi-scst-adm
index 5af7c28..a538350 100644 (file)
@@ -307,7 +307,8 @@ static inline void chap_encode_string(u8 *intnum, int buf_len, char *encode_buf,
        }
 }
 
-static inline void chap_calc_digest_md5(char chap_id, char *secret, int secret_len, u8 *challenge, int challenge_len, u8 *digest)
+static inline void chap_calc_digest_md5(char chap_id, const char *secret, int secret_len,
+       const u8 *challenge, int challenge_len, u8 *digest)
 {
        MD5_CTX ctx;
 
@@ -318,7 +319,8 @@ static inline void chap_calc_digest_md5(char chap_id, char *secret, int secret_l
        MD5_Final(digest, &ctx);
 }
 
-static inline void chap_calc_digest_sha1(char chap_id, char *secret, int secret_len, u8 *challenge, int challenge_len, u8 *digest)
+static inline void chap_calc_digest_sha1(char chap_id, const char *secret, int secret_len,
+       const u8 *challenge, int challenge_len, u8 *digest)
 {
        SHA_CTX ctx;
 
@@ -389,8 +391,7 @@ static int chap_initiator_auth_check_response(struct connection *conn)
        int digest_len = 0, retval = 0, encoding_format;
        char pass[ISCSI_NAME_LEN];
 
-       memset(pass, 0, sizeof(pass));
-       if (config_account_query(conn->tid, AUTH_DIR_INCOMING, pass, pass) < 0) {
+       if (accounts_empty(conn->tid, ISCSI_USER_DIR_INCOMING)) {
                log_warning("CHAP initiator auth.: "
                            "No CHAP credentials configured");
                retval = CHAP_TARGET_ERROR;
@@ -408,7 +409,7 @@ static int chap_initiator_auth_check_response(struct connection *conn)
        }
 
        memset(pass, 0, sizeof(pass));
-       if (config_account_query(conn->tid, AUTH_DIR_INCOMING, value, pass) < 0) {
+       if (config_account_query(conn->tid, ISCSI_USER_DIR_INCOMING, value, pass) < 0) {
                log_warning("CHAP initiator auth.: "
                            "No valid user/pass combination for initiator %s "
                            "found", conn->initiator);
@@ -493,19 +494,18 @@ static int chap_target_auth_create_response(struct connection *conn)
        u8 *challenge = NULL, *digest = NULL;
        int encoding_format, response_len;
        int challenge_len = 0, digest_len = 0, retval = 0;
-       char pass[ISCSI_NAME_LEN], name[ISCSI_NAME_LEN];
+       struct iscsi_user *user;
 
        if (!(value = text_key_find(conn, "CHAP_I"))) {
-               /* initiator doesn't want target auth!? */
+               /* Initiator doesn't want target auth!? */
                conn->state = STATE_SECURITY_DONE;
                retval = 0;
                goto out;
        }
        chap_id = strtol(value, &value, 10);
 
-       memset(pass, 0, sizeof(pass));
-       memset(name, 0, sizeof(name));
-       if (config_account_query(conn->tid, AUTH_DIR_OUTGOING, name, pass) < 0) {
+       user = account_get_first(conn->tid, ISCSI_USER_DIR_OUTGOING);
+       if (user == NULL) {
                log_warning("CHAP target auth.: "
                            "no outgoing credentials configured%s",
                            conn->tid ? "." : " for discovery.");
@@ -588,11 +588,11 @@ static int chap_target_auth_create_response(struct connection *conn)
 
        switch (conn->auth.chap.digest_alg) {
        case CHAP_DIGEST_ALG_MD5:
-               chap_calc_digest_md5(chap_id, pass, strlen(pass),
+               chap_calc_digest_md5(chap_id, user->password, strlen(user->password),
                                     challenge, challenge_len, digest);
                break;
        case CHAP_DIGEST_ALG_SHA1:
-               chap_calc_digest_sha1(chap_id, pass, strlen(pass),
+               chap_calc_digest_sha1(chap_id, user->password, strlen(user->password),
                                      challenge, challenge_len, digest);
                break;
        default:
@@ -602,7 +602,7 @@ static int chap_target_auth_create_response(struct connection *conn)
 
        memset(response, 0x0, response_len);
        chap_encode_string(digest, digest_len, response, encoding_format);
-       text_key_add(conn, "CHAP_N", name);
+       text_key_add(conn, "CHAP_N", user->name);
        text_key_add(conn, "CHAP_R", response);
 
        conn->state = STATE_SECURITY_DONE;
index e7ff36b..5d27ca5 100644 (file)
 #include <dirent.h>
 #include <errno.h>
 #include <netdb.h>
+#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
 
 
 #define BUFSIZE                4096
 #define CONFIG_FILE    "/etc/iscsi-scstd.conf"
-#define ACCT_CONFIG_FILE       CONFIG_FILE
 
-/*
- * Account configuration code
- */
+/* Index must match ISCSI_USER_DIR_*!! */
+struct iscsi_key user_keys[] = {
+       {"IncomingUser",},
+       {"OutgoingUser",},
+       {NULL,},
+};
 
-struct user {
-       struct __qelem ulist;
+static struct __qelem discovery_users_in = LIST_HEAD_INIT(discovery_users_in);
+static struct __qelem discovery_users_out = LIST_HEAD_INIT(discovery_users_out);
 
-       u32 tid;
-       char *name;
-       char *password;
-};
+static struct __qelem *account_list_get(struct target *target, int dir)
+{
+       struct __qelem *list = NULL;
+
+       if (target != NULL) {
+               list = (dir == ISCSI_USER_DIR_INCOMING) ?
+                       &target->target_in_accounts : &target->target_out_accounts;
+       } else
+               list = (dir == ISCSI_USER_DIR_INCOMING) ?
+                       &discovery_users_in : &discovery_users_out;
 
-/* this is the orignal Ardis code. */
-static char *target_sep_string(char **pp)
+       return list;
+}
+
+char *config_sep_string(char **pp)
 {
        char *p = *pp;
        char *q;
+       static char blank = '\0';
+
+       if ((pp == NULL) || (*pp == NULL))
+               return &blank;
 
-       for (p = *pp; isspace(*p); p++)
+       for (p = *pp; isspace(*p) || (*p == '='); p++)
                ;
-       for (q = p; *q && !isspace(*q); q++)
+
+       for (q = p; (*q != '\0') && !isspace(*q) && (*q != '='); q++)
                ;
-       if (*q)
-               *q++ = 0;
-       else
-               p = NULL;
+
+       if (*q != '\0')
+               *q++ = '\0';
+
        *pp = q;
        return p;
 }
 
-static struct iscsi_key user_keys[] = {
-       {"IncomingUser",},
-       {"OutgoingUser",},
-       {NULL,},
-};
+static char *config_gets(char *buf, int size, const char *data, int *offset)
+{
+       int offs = *offset, i = 0;
 
-static struct __qelem discovery_users_in = LIST_HEAD_INIT(discovery_users_in);
-static struct __qelem discovery_users_out = LIST_HEAD_INIT(discovery_users_out);
+       while ((i < size-1) && (data[offs] != '\n') && (data[offs] != ';') && (data[offs] != '\0'))
+               buf[i++] = data[offs++];
 
-#define HASH_ORDER     4
-#define acct_hash(x)   ((x) & ((1 << HASH_ORDER) - 1))
+       if ((i == 0) && (data[offs] == '\0'))
+               return NULL;
 
-static struct __qelem trgt_acct_in[1 << HASH_ORDER];
-static struct __qelem trgt_acct_out[1 << HASH_ORDER];
+       if (data[offs] != '\0')
+               offs++;
 
-static struct __qelem *account_list_get(u32 tid, int dir)
+       *offset = offs;
+       buf[i] = '\0';
+
+       return buf;
+}
+
+int accounts_empty(u32 tid, int dir)
 {
-       struct __qelem *list = NULL;
+       struct target *target;
+       struct __qelem *list;
 
        if (tid) {
-               list = (dir == AUTH_DIR_INCOMING) ?
-                       &trgt_acct_in[acct_hash(tid)] : &trgt_acct_out[acct_hash(tid)];
+               target = target_find_by_id(tid);
+               if (target == NULL)
+                       return 0;
        } else
-               list = (dir == AUTH_DIR_INCOMING) ?
-                       &discovery_users_in : &discovery_users_out;
+               target = NULL;
 
-       return list;
+       list = account_list_get(target, dir);
+
+       return list_empty(list);
 }
 
-static int config_account_init(char *filename)
+static struct iscsi_user *__account_lookup_by_name(struct target *target,
+       int dir, const char *name)
 {
-       FILE *fp;
-       char buf[BUFSIZE], *p, *q;
-       u32 tid;
-       int idx, res = 0;
+       struct __qelem *list;
+       struct iscsi_user *user = NULL;
 
-       if (!(fp = fopen(filename, "r"))) {
-               return errno == ENOENT ? 0 : -errno;
+       list = account_list_get(target, dir);
+
+       list_for_each_entry(user, list, ulist) {
+               if (!strcmp(user->name, name))
+                       return user;
        }
 
-       tid = 0;
-       while (fgets(buf, sizeof(buf), fp)) {
-               q = buf;
-               p = target_sep_string(&q);
-               if (!p || *p == '#')
-                       continue;
+       return NULL;
+}
 
-               if (!strcasecmp(p, "Target")) {
-                       tid = 0;
-                       if (!(p = target_sep_string(&q)))
-                               continue;
-                       tid = target_find_id_by_name(p);
-               } else if (!((idx = param_index_by_name(p, user_keys)) < 0)) {
-                       char *name, *pass;
-                       name = target_sep_string(&q);
-                       pass = target_sep_string(&q);
 
-                       res = config_account_add(tid, idx, name, pass);
-                       if (res < 0) {
-                               log_error("%s %s\n", name, pass);
-                               break;
-                       }
-               }
-       }
+static struct iscsi_user *account_lookup_by_name(u32 tid, int dir, const char *name)
+{
+       struct target *target;
 
-       fclose(fp);
+       if (tid) {
+               target = target_find_by_id(tid);
+               if (target == NULL)
+                       return NULL;
+       } else
+               target = NULL;
 
-       return res;
+       return __account_lookup_by_name(target, dir, name);
 }
 
-/* Return the first account if the length of name is zero */
-static struct user *account_lookup_by_name(u32 tid, int dir, char *name)
+struct iscsi_user *account_get_first(u32 tid, int dir)
 {
-       struct __qelem *list = account_list_get(tid, dir);
-       struct user *user = NULL;
+       struct target *target;
+       struct __qelem *list;
+       struct iscsi_user *user = NULL;
+
+       if (tid) {
+               target = target_find_by_id(tid);
+               if (target == NULL)
+                       return NULL;
+       } else
+               target = NULL;
+
+       list = account_list_get(target, dir);
 
        list_for_each_entry(user, list, ulist) {
-               if (user->tid != tid)
-                       continue;
-               if (!strlen(name))
-                       return user;
-               if (!strcmp(user->name, name))
+               return user;
+       }
+
+       return NULL;
+}
+
+struct iscsi_user *account_lookup_by_sysfs_name(struct target *target,
+       int dir, const char *sysfs_name)
+{
+       struct __qelem *list;
+       struct iscsi_user *user = NULL;
+
+       list = account_list_get(target, dir);
+
+       list_for_each_entry(user, list, ulist) {
+               if (!strcmp(user->sysfs_name, sysfs_name))
                        return user;
        }
 
        return NULL;
 }
 
-int config_account_query(u32 tid, int dir, char *name, char *pass)
+int config_account_query(u32 tid, int dir, const char *name, char *pass)
 {
-       struct user *user;
+       struct iscsi_user *user;
 
        if (!(user = account_lookup_by_name(tid, dir, name)))
                return -ENOENT;
 
-       if (!strlen(name))
-               strncpy(name, user->name, ISCSI_NAME_LEN);
-
-       strncpy(pass, user->password, ISCSI_NAME_LEN);
+       strlcpy(pass, user->password, ISCSI_NAME_LEN);
 
        return 0;
 }
@@ -170,19 +201,27 @@ int config_account_query(u32 tid, int dir, char *name, char *pass)
 int config_account_list(u32 tid, int dir, u32 *cnt, u32 *overflow,
        char *buf, size_t buf_sz)
 {
-       struct __qelem *list = account_list_get(tid, dir);
-       struct user *user;
+       struct target *target;
+       struct __qelem *list;
+       struct iscsi_user *user;
 
        *cnt = *overflow = 0;
 
+       if (tid) {
+               target = target_find_by_id(tid);
+               if (target == NULL)
+                       return -ENOENT;
+       } else
+               target = NULL;
+
+       list = account_list_get(target, dir);
+
        if (!list)
                return -ENOENT;
 
        list_for_each_entry(user, list, ulist) {
-               if (user->tid != tid)
-                       continue;
                if (buf_sz >= ISCSI_NAME_LEN) {
-                       strncpy(buf, user->name, ISCSI_NAME_LEN);
+                       strlcpy(buf, user->name, ISCSI_NAME_LEN);
                        buf_sz -= ISCSI_NAME_LEN;
                        buf += ISCSI_NAME_LEN;
                        *cnt += 1;
@@ -193,89 +232,255 @@ int config_account_list(u32 tid, int dir, u32 *cnt, u32 *overflow,
        return 0;
 }
 
-static void account_destroy(struct user *user)
+static void account_destroy(struct iscsi_user *user, int del)
 {
        if (!user)
                return;
-       remque(&user->ulist);
-       free(user->name);
-       free(user->password);
+       if (del)
+               list_del(&user->ulist);
+       free((void *)user->name);
+       free((void *)user->password);
        free(user);
+       return;
 }
 
-int config_account_del(u32 tid, int dir, char *name)
+void accounts_free(struct __qelem *accounts_list)
 {
-       struct user *user;
+       struct iscsi_user *user, *t;
 
-       if (!name || !(user = account_lookup_by_name(tid, dir, name)))
-               return -ENOENT;
+       list_for_each_entry_safe(user, t, accounts_list, ulist) {
+               account_destroy(user, 1);
+       }
 
-       account_destroy(user);
+       return;
+}
 
-       /* update the file here. */
-       return 0;
+int config_account_del(u32 tid, int dir, char *name, u32 cookie)
+{
+       struct iscsi_user *user;
+       int res = 0;
+
+       if (!name) {
+               log_error("%s", "Name expected");
+               res = -EINVAL;
+               goto out;
+       }
+
+       user = account_lookup_by_name(tid, dir, name);
+       if (user == NULL) {
+               log_error("User %s not found", name);
+               res = -ENOENT;
+               goto out;
+       }
+
+#ifndef CONFIG_SCST_PROC
+       res = kernel_user_del(user, cookie);
+       if (res != 0)
+               goto out;
+#endif
+
+       account_destroy(user, 1);
+
+out:
+       return res;
 }
 
-static struct user *account_create(void)
+static struct iscsi_user *account_create(struct target *target, int direction,
+       const char *sysfs_name, const char *name, const char *pass)
 {
-       struct user *user;
+       struct iscsi_user *user;
 
        if (!(user = malloc(sizeof(*user))))
                return NULL;
 
        memset(user, 0, sizeof(*user));
        INIT_LIST_HEAD(&user->ulist);
+       user->target = target;
+       user->direction = direction;
+
+       if (!(user->name = strdup(name)) ||
+           !(user->password = strdup(pass))) {
+               log_error("Unable to duplicate name (%s) or password (%s)",
+                       name, pass);
+               goto out_destroy;
+       }
+
+       if (direction == ISCSI_USER_DIR_INCOMING) {
+               int inc_user_num = 0;
 
+               if (sysfs_name != NULL) {
+                       strlcpy(user->sysfs_name, sysfs_name, sizeof(user->sysfs_name));
+                       if (account_lookup_by_sysfs_name(target, direction, sysfs_name) == NULL)
+                               goto out;
+               }
+
+               while (1) {
+                       if (inc_user_num == 0)
+                               snprintf(user->sysfs_name, sizeof(user->sysfs_name),
+                                       "IncomingUser");
+                       else
+                               snprintf(user->sysfs_name, sizeof(user->sysfs_name),
+                                       "IncomingUser%d", inc_user_num);
+                       if (account_lookup_by_sysfs_name(target, direction, user->sysfs_name) == NULL)
+                               break;
+                       inc_user_num++;
+               }
+       } else
+               snprintf(user->sysfs_name, sizeof(user->sysfs_name),
+                       "OutgoingUser");
+
+out:
        return user;
+
+out_destroy:
+       account_destroy(user, 0);
+       user = NULL;
+       goto out;
 }
 
-int config_account_add(u32 tid, int dir, char *name, char *pass)
+int account_replace(struct target *target, int direction,
+       const char *sysfs_name, char *value)
 {
-       int err = -ENOMEM;
-       struct user *user;
+       int res = 0;
+       struct iscsi_user *user, *user1;
+       char *name, *pass, *n;
        struct __qelem *list;
 
-       if (!name || !pass)
-               return -EINVAL;
+       name = config_sep_string(&value);
+       pass = config_sep_string(&value);
 
-       if (tid) {
-               /* check here */
-               /* return -ENOENT; */
+       n = config_sep_string(&value);
+       if (*n != '\0') {
+               log_error("Unexpected parameter value %s\n", n);
+               res = -EINVAL;
+               goto out;
+       }
+
+       user = account_lookup_by_sysfs_name(target, direction, sysfs_name);
+       if (user == NULL) {
+               log_error("Unknown parameter %s\n", sysfs_name);
+               res = -EINVAL;
+               goto out;
+       }
+
+       user1 = __account_lookup_by_name(target, direction, name);
+       if ((user1 != NULL) && (user1 != user)) {
+               log_error("User %s already exists\n", name);
+               res = -EEXIST;
+               goto out;
+       }
+
+       list = account_list_get(target, direction);
+
+       list_del(&user->ulist);
+
+       user1 = account_create(target, direction, sysfs_name, name, pass);
+       if (user1 == NULL) {
+               res = -ENOMEM;
+               goto out_add;
+       }
+
+       list_add_tail(user1, list);
+
+       account_destroy(user, 0);
+
+out:
+       return res;
+
+out_add:
+       list_add_tail(user, list);
+       goto out;
+}
+
+int __config_account_add(struct target *target, int dir, char *name,
+       char *pass, char *sysfs_name, int send_to_kern, u32 cookie)
+{
+       int err = 0;
+       struct iscsi_user *user;
+       struct __qelem *list;
+       int del = 0;
+
+       if (!name || !pass) {
+               log_error("%s", "Name or password is NULL");
+               err = -EINVAL;
+               goto out;
        }
 
        /* Check for minimum RFC defined value */
        if (strlen(pass) < 12) {
                log_error("Secret for user %s is too short. At least 12 bytes "
                        "are required\n", name);
-               return -EINVAL;
+               err = -EINVAL;
+               goto out;
        }
 
-       if (!(user = account_create()) ||
-           !(user->name = strdup(name)) ||
-           !(user->password = strdup(pass)))
+       user = account_create(target, dir, sysfs_name, name, pass);
+       if (user == NULL) {
+               err = -ENOMEM;
                goto out;
+       }
 
-       user->tid = tid;
-       list = account_list_get(tid, dir);
-       if (dir == AUTH_DIR_OUTGOING) {
-               struct user *old, *tmp;
-               list_for_each_entry_safe(old, tmp, list, ulist) {
-                       if (tid != old->tid)
-                               continue;
-                       log_warning("Only one outgoing %s account is supported."
-                                   " Replacing the old one.\n",
-                                   tid ? "target" : "discovery");
-                       account_destroy(old);
+       if (__account_lookup_by_name(target, dir, name) != NULL) {
+               log_error("User %s already exists for target %s (direction %s)",
+                       name, target ? target->name : "discovery",
+                       (dir == ISCSI_USER_DIR_OUTGOING) ? "outgoing" : "incoming");
+               err = -EEXIST;
+               goto out_destroy;
+       }
+
+       list = account_list_get(target, dir);
+       if (dir == ISCSI_USER_DIR_OUTGOING) {
+               struct iscsi_user *old;
+               list_for_each_entry(old, list, ulist) {
+                       log_warning("Only one outgoing %s account is "
+                               "supported. Replacing the old one.\n",
+                               target ? "target" : "discovery");
+                       account_destroy(old, 1);
+                       break;
                }
        }
 
-       insque(user, list);
+       log_debug(1, "User %s added to target %s (direction %s)", user->name,
+               target ? target->name : "discovery",
+               (dir == ISCSI_USER_DIR_OUTGOING) ? "outgoing" : "incoming");
+
+       list_add_tail(user, list);
+       del = 1;
+
+#ifndef CONFIG_SCST_PROC
+       if (send_to_kern) {
+               err = kernel_user_add(user, cookie);
+               if (err != 0)
+                       goto out_destroy;
+       }
+#endif
 
-       /* update the file here. */
-       return 0;
 out:
-       account_destroy(user);
+       return err;
+
+out_destroy:
+       account_destroy(user, del);
+       goto out;
+}
+
+int config_account_add(u32 tid, int dir, char *name, char *pass, char *sysfs_name,
+       u32 cookie)
+{
+       int err = 0;
+       struct target *target;
 
+       if (tid) {
+               target = target_find_by_id(tid);
+               if (target == NULL) {
+                       err = -ENOENT;
+                       goto out;
+               }
+       } else
+               target = NULL;
+
+       err = __config_account_add(target, dir, name, pass, sysfs_name, 1, cookie);
+
+out:
        return err;
 }
 
@@ -467,167 +672,390 @@ int config_initiator_access(u32 tid, int fd)
  * Main configuration code
  */
 
-static int __config_target_create(u32 *tid, char *name, int update)
+int config_target_create(u32 *tid, char *name)
 {
        int err;
+       struct target *target;
 
-       if (target_find_by_name(name)) {
-               log_error("duplicated target %s", name);
-               return -EINVAL;
-       }
-       if ((err = target_add(tid, name)) < 0)
-               return err;
+       err = target_create(name, &target);
+       if (err != 0)
+               goto out;
+
+       err = target_add(target, tid, 0);
+       if (err != 0)
+               goto out_free;
 
+out:
        return err;
-}
 
-int config_target_create(u32 *tid, char *name)
-{
-       return __config_target_create(tid, name, 1);
+out_free:
+       target_free(target);
+       goto out;
 }
 
 int config_target_destroy(u32 tid)
 {
        int err;
 
-       if ((err = target_del(tid)) < 0)
+       if ((err = target_del(tid, 0)) < 0)
                return err;
 
        return err;
 }
 
-int config_param_set(u32 tid, u64 sid, int type, u32 partial,
-       struct iscsi_param *param)
+int config_params_get(u32 tid, u64 sid, int type, struct iscsi_param *params)
 {
-       int err;
+       int err, i;
+       struct target *target;
 
-       err = kernel_param_set(tid, sid, type, partial, param);
+       if (sid != 0) {
+               err = kernel_params_get(tid, sid, type, params);
+               goto out;
+       }
 
+       err = 0;
+
+       target = target_find_by_id(tid);
+       if (target == NULL) {
+               log_error("target %d not found", tid);
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (type == key_session) {
+               for (i = 0; i < session_key_last; i++)
+                       params[i].val = target->session_params[i];
+       } else {
+               for (i = 0; i < target_key_last; i++)
+                               params[i].val = target->target_params[i];
+       }
+
+out:
        return err;
 }
 
-static int iscsi_param_partial_set(u32 tid, u64 sid, int type, int key, u32 val)
+int config_params_set(u32 tid, u64 sid, int type, u32 partial,
+       struct iscsi_param *params)
 {
-       struct iscsi_param *param;
-       struct iscsi_param session_param[session_key_last];
-       struct iscsi_param target_param[target_key_last];
+       int err, i;
+       struct target *target;
 
-       if (type == key_session)
-               param = session_param;
-       else
-               param = target_param;
+       if (sid != 0) {
+               err = kernel_params_set(tid, sid, type, partial, params);
+               goto out;
+       }
 
-       param[key].val = val;
+       err = 0;
 
-       return config_param_set(tid, sid, type, 1 << key, param);
+       target = target_find_by_id(tid);
+       if (target == 0) {
+               log_error("target %d not found", tid);
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (partial == 0)
+               partial = (typeof(partial))-1;
+
+       if (type == key_session) {
+               for (i = 0; i < session_key_last; i++) {
+                       if (partial & (1 << i)) {
+                               err = params_check_val(session_keys, i, &params[i].val);
+                               if (err < 0) {
+                                       log_error("Wrong value %u for parameter %s\n",
+                                               params[i].val, session_keys[i].name);
+                                       goto out;
+                               }
+                       }
+               }
+               for (i = 0; i < session_key_last; i++) {
+                       if (partial & (1 << i))
+                               target->session_params[i] = params[i].val;
+               }
+       } else {
+               for (i = 0; i < target_key_last; i++) {
+                       if (partial & (1 << i)) {
+                               err = params_check_val(target_keys, i, &params[i].val);
+                               if (err < 0) {
+                                       log_error("Wrong value %u for parameter %s\n",
+                                               params[i].val, target_keys[i].name);
+                                       goto out;
+                               }
+                       }
+               }
+               for (i = 0; i < target_key_last; i++) {
+                       if (partial & (1 << i))
+                               target->target_params[i] = params[i].val;
+               }
+       }
+
+out:
+       return err;
 }
 
-static int config_main_init(char *filename)
+int config_parse_main(const char *data, u32 cookie)
 {
-       FILE *config;
        char buf[BUFSIZE];
-       char *p, *q;
-       int idx;
-       u32 tid, val;
+       char *p, *q, *n;
+       int idx, offset = 0;
+       u32 val;
        int res = 0;
+       struct target *target = NULL;
+       int global_section = 1; /* supposed to be bool and true */
+       int parsed_something = 0; /* supposed to be bool and false */
+       int stop_on_errors = (cookie != 0);
+
+       while (config_gets(buf, sizeof(buf), data, &offset)) {
+               parsed_something = 1;
+               /*
+                * If stop_on_errors is false, let's always continue parsing
+                * and only report errors.
+                */
+               if (stop_on_errors && (res != 0))
+                       goto out_target_free;
 
-       if (!(config = fopen(filename, "r"))) {
-               return errno == ENOENT ? 0 : -errno;
-       }
-
-       tid = 0;
-       while (fgets(buf, BUFSIZE, config)) {
                q = buf;
-               p = target_sep_string(&q);
-               if (!p || *p == '#')
+               p = config_sep_string(&q);
+               if ((*p == '#') || (*p == '\0'))
                        continue;
+
                if (!strcasecmp(p, "Target")) {
-                       tid = 0;
-                       if (!(p = target_sep_string(&q)))
+                       global_section = 0;
+
+                       if (target != NULL) {
+                               res = target_add(target, NULL, cookie);
+                               if (res != 0)
+                                       target_free(target);
+                       }
+
+                       target = NULL;
+                       p = config_sep_string(&q);
+                       if (p == '\0') {
+                               log_error("Target name required on %s\n", q);
                                continue;
-                       if (__config_target_create(&tid, p, 0))
-                               log_debug(1, "creating target %s", p);
-               } else if (!strcasecmp(p, "Alias") && tid) {
+                       }
+
+                       n = config_sep_string(&q);
+                       if (*n != '\0') {
+                               log_error("Unexpected parameter value %s\n", n);
+                               res = -EINVAL;
+                               continue;
+                       }
+
+                       log_debug(1, "Creating target %s", p);
+                       res = target_create(p, &target);
+                       if (res != 0)
+                               goto out;
+               } else if (!strcasecmp(p, "Alias") && target) {
                        ;
-               } else if (!((idx = param_index_by_name(p, target_keys)) < 0) && tid) {
-                       val = strtol(q, &q, 0);
-                       if (param_check_val(target_keys, idx, &val) < 0) {
+               } else if (!((idx = params_index_by_name(p, target_keys)) < 0) && (target != NULL)) {
+                       char *str = config_sep_string(&q);
+
+                       n = config_sep_string(&q);
+                       if (*n != '\0') {
+                               log_error("Unexpected parameter value %s\n", n);
+                               res = -EINVAL;
+                               continue;
+                       }
+
+                       res = params_str_to_val(target_keys, idx, str, &val);
+                       if (res < 0) {
+                               log_error("Wrong value %s for parameter %s\n",
+                                       str, target_keys[idx].name);
+                               continue;
+                       }
+
+                       res = params_check_val(target_keys, idx, &val);
+                       if (res < 0) {
                                log_error("Wrong value %u for parameter %s\n",
                                        val, target_keys[idx].name);
-                               res = -1;
-                               break;
+                               continue;
                        }
-                       iscsi_param_partial_set(tid, 0, key_target, idx, val);
-               } else if (!((idx = param_index_by_name(p, session_keys)) < 0) && tid) {
-                       char *str = target_sep_string(&q);
-                       if (param_str_to_val(session_keys, idx, str, &val) < 0) {
+                       target->target_params[idx] = val;
+               } else if (!((idx = params_index_by_name(p, session_keys)) < 0) && (target != NULL)) {
+                       char *str = config_sep_string(&q);
+
+                       n = config_sep_string(&q);
+                       if (*n != '\0') {
+                               log_error("Unexpected parameter value %s\n", n);
+                               res = -EINVAL;
+                               continue;
+                       }
+
+                       res = params_str_to_val(session_keys, idx, str, &val);
+                       if (res < 0) {
                                log_error("Wrong value %s for parameter %s\n",
                                        str, session_keys[idx].name);
-                               res = -1;
-                               break;
+                               continue;
                        }
-                       if (param_check_val(session_keys, idx, &val) < 0) {
+
+                       res = params_check_val(session_keys, idx, &val);
+                       if (res < 0) {
                                log_error("Wrong value %u for parameter %s\n",
                                        val, session_keys[idx].name);
-                               res = -1;
-                               break;
+                               continue;
                        }
-                       iscsi_param_partial_set(tid, 0, key_session, idx, val);
-               } else if (param_index_by_name(p, user_keys) < 0) {
-                       log_warning("Unknown iscsi-scstd.conf param: %s\n", p);
-                       res = -1;
-                       break;
-               }
-       }
+                       target->session_params[idx] = val;
+               } else if (!((idx = params_index_by_name_numwild(p, user_keys)) < 0) &&
+                          ((target != NULL) || global_section)) {
+                       char *name, *pass;
 
-       fclose(config);
-       return res;
-}
+                       name = config_sep_string(&q);
+                       pass = config_sep_string(&q);
 
-int config_load(char *params)
-{
-       int i, err;
+                       n = config_sep_string(&q);
+                       if (*n != '\0') {
+                               log_error("Unexpected parameter value %s\n", n);
+                               res = -EINVAL;
+                               continue;
+                       }
 
-       for (i = 0; i < 1 << HASH_ORDER; i++) {
-               INIT_LIST_HEAD(&trgt_acct_in[i]);
-               INIT_LIST_HEAD(&trgt_acct_out[i]);
+                       res = __config_account_add(target, idx, name, pass, p,
+                                       (target == 0), 0);
+                       if (res < 0)
+                               continue;
+               } else if (global_section &&
+                          (!strcasecmp(p, ISCSI_ISNS_SERVER_PARAM_NAME) ||
+                           !strcasecmp(p, ISCSI_ISNS_ACCESS_CONTROL_PARAM_NAME)))
+                       continue;
+               else {
+                       log_error("Unknown or unexpected param: %s\n", p);
+                       res = -EINVAL;
+                       continue;
+               }
        }
 
-       /* First, we must finish the main configuration. */
-       if ((err = config_main_init(params ? params : CONFIG_FILE)))
-               return err;
+       if (stop_on_errors && (res != 0))
+               goto out_target_free;
 
-       if ((err = config_account_init(ACCT_CONFIG_FILE)) < 0)
-               return err;
+       if (target != NULL) {
+               res = target_add(target, NULL, cookie);
+               if (res != 0)
+                       goto out_target_free;
+       }
 
-       /* TODO: error handling */
+out:
+       if (stop_on_errors) {
+               if ((res == 0) && !parsed_something)
+                       res = -ENOENT;
+       } else
+               res = 0;
 
-       return err;
+       return res;
+
+out_target_free:
+       if (target != NULL)
+               target_free(target);
+       goto out;
 }
 
-int config_isns_load(char *params, char **isns, int *isns_ac)
+static int config_isns_load(const char *config)
 {
-       FILE *config;
        char buf[BUFSIZE];
+       int offset = 0;
        char *p, *q;
 
-       if (!(config = fopen(params ? : CONFIG_FILE, "r")))
-               return -errno;
-
-       while (fgets(buf, BUFSIZE, config)) {
+       while (config_gets(buf, sizeof(buf), config, &offset)) {
                q = buf;
-               p = target_sep_string(&q);
-               if (!p || *p == '#')
+               p = config_sep_string(&q);
+               if ((*p == '\0') || (*p == '#'))
                        continue;
-               if (!strcasecmp(p, "iSNSServer")) {
-                       *isns = strdup(target_sep_string(&q));
-               } else if (!strcasecmp(p, "iSNSAccessControl")) {
-                       char *str = target_sep_string(&q);
-                       if (!strcasecmp(str, "Yes"))
-                               *isns_ac = 1;
+               if (!strcasecmp(p, ISCSI_ISNS_SERVER_PARAM_NAME)) {
+                       isns_server = strdup(config_sep_string(&q));
+               } else if (!strcasecmp(p, ISCSI_ISNS_ACCESS_CONTROL_PARAM_NAME)) {
+                       char *str = config_sep_string(&q);
+                       if (!strcasecmp(str, "No"))
+                               isns_access_control = 0;
+                       else
+                               isns_access_control = 1;
                }
        }
 
-       fclose(config);
        return 0;
 }
+
+int config_load(const char *config_name)
+{
+       int i, err = 0, rc;
+       int config;
+       const char *cname;
+       int size;
+       char *buf;
+
+       if (config_name != NULL)
+               cname = config_name;
+       else
+               cname = CONFIG_FILE;
+
+       config = open(cname, O_RDONLY);
+       if (config == -1) {
+               if ((errno == ENOENT) && (config_name == NULL)) {
+#ifdef CONFIG_SCST_PROC
+                       log_debug(3, "Default config file %s not found",
+                               CONFIG_FILE);
+#endif
+                       goto out;
+               } else {
+                       err = -errno;
+                       log_error("Open config file %s failed: %s", cname,
+                               strerror(err));
+                       goto out;
+               }
+       }
+
+       size = lseek(config, 0, SEEK_END);
+       if (size < 0) {
+               err = -errno;
+               log_error("lseek() failed: %s", strerror(err));
+               goto out_close;
+       }
+
+       buf = malloc(size+1);
+       if (buf == NULL) {
+               err = -ENOMEM;
+               log_error("malloc() failed: %s", strerror(err));
+               goto out_close;
+       }
+
+       rc = lseek(config, 0, SEEK_SET);
+       if (rc < 0) {
+               err = -errno;
+               log_error("lseek() failed: %s", strerror(err));
+               goto out_free;
+       }
+
+       i = 0;
+       do {
+               rc = read(config, &buf[i], size - i);
+               if (rc < 0) {
+                       err = -errno;
+                       log_error("read() failed: %s", strerror(err));
+                       goto out_free;
+               } else if (rc == 0)
+                       break;
+               i += rc;
+       } while (i < size);
+
+       size = i;
+       buf[size+1] = '\0';
+
+       config_parse_main(buf, 0);
+
+       err = config_isns_load(buf);
+       if ((err == 0) && (isns_server != NULL)) {
+               int rc = isns_init();
+               if (rc != 0) {
+                       log_error("iSNS server %s init failed: %d", isns_server, rc);
+                       isns_exit();
+               }
+       }
+
+out_free:
+       free(buf);
+
+out_close:
+       close(config);
+
+out:
+       return err;
+}
index d89aefd..f4a6c74 100644 (file)
@@ -30,6 +30,8 @@
 struct connection *conn_alloc(void)
 {
        struct connection *conn;
+       unsigned int session_params[session_key_last];
+       int i;
 
        conn = malloc(sizeof(*conn));
        if (conn == NULL)
@@ -37,16 +39,20 @@ struct connection *conn_alloc(void)
 
        memset(conn, 0, sizeof(*conn));
        conn->state = STATE_FREE;
-       param_set_defaults(conn->session_param, session_keys);
        INIT_LIST_HEAD(&conn->rsp_buf_list);
 
+       params_set_defaults(session_params, session_keys);
+
+       for (i = 0; i < session_key_last; i++)
+               conn->session_params[i].val = session_params[i];
+
 out:
        return conn;
 }
 
 void conn_free(struct connection *conn)
 {
-       remque(&conn->clist);
+       list_del(&conn->clist);
        free(conn->initiator);
        free(conn->user);
        free(conn);
index 3557e25..dd34f72 100644 (file)
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -36,7 +37,7 @@ int kernel_open(int *max_data_seg_len)
        int devn;
        int ctlfd = -1;
        int err;
-       struct iscsi_kern_register_info reg = { 0 };
+       struct iscsi_kern_register_info reg;
 
        if (!(f = fopen("/proc/devices", "r"))) {
                err = -errno;
@@ -80,17 +81,18 @@ int kernel_open(int *max_data_seg_len)
                goto out_err;
        }
 
+       memset(&reg, 0, sizeof(reg));
        reg.version = (uintptr_t)ISCSI_SCST_INTERFACE_VERSION;
 
        err = ioctl(ctlfd, REGISTER_USERD, &reg);
-       if (err < 0) {
+       if (err != 0) {
                err = -errno;
                log_error("Unable to register: %s. Incompatible version of the "
                        "kernel module?\n", strerror(errno));
                goto out_close;
        } else {
                log_debug(0, "MAX_DATA_SEG_LEN %d", err);
-               *max_data_seg_len = err;
+               *max_data_seg_len = reg.max_data_seg_len;
        }
 
 out:
@@ -104,32 +106,98 @@ out_err:
        goto out;
 }
 
-int kernel_target_create(u32 *tid, char *name)
+int kernel_target_create(struct target *target, u32 *tid, u32 cookie)
 {
-       int err;
+       int err, i, j;
        struct iscsi_kern_target_info info;
+       struct iscsi_user *user;
+       struct iscsi_kern_attr *kern_attrs;
 
        memset(&info, 0, sizeof(info));
+       strlcpy(info.name, target->name, sizeof(info.name));
+       info.tid = (tid != NULL) ? *tid : 0;
+       info.cookie = cookie;
+
+       for (j = 0; j < session_key_last; j++) {
+               if (session_keys[j].show_in_sysfs)
+                       info.attrs_num++;;
+       }
+       for (j = 0; j < target_key_last; j++) {
+               if (target_keys[j].show_in_sysfs)
+                       info.attrs_num++;;
+       }
+       list_for_each_entry(user, &target->target_in_accounts, ulist) {
+               info.attrs_num++;
+       }
+       list_for_each_entry(user, &target->target_out_accounts, ulist) {
+               info.attrs_num++;
+       }
+
+       kern_attrs = calloc(info.attrs_num, sizeof(*kern_attrs));
+       if (kern_attrs == NULL) {
+               err = -ENOMEM;
+               goto out;
+       }
+       info.attrs_ptr = (unsigned long)kern_attrs;
+
+       i = 0;
+       for (j = 0; j < session_key_last; j++) {
+               if (!session_keys[j].show_in_sysfs)
+                       continue;
+               kern_attrs[i].mode = 0644;
+               strlcpy(kern_attrs[i].name, session_keys[j].name,
+                       sizeof(kern_attrs[i].name));
+               i++;
+       }
+       for (j = 0; j < target_key_last; j++) {
+               if (!target_keys[j].show_in_sysfs)
+                       continue;
+               kern_attrs[i].mode = 0644;
+               strlcpy(kern_attrs[i].name, target_keys[j].name,
+                       sizeof(kern_attrs[i].name));
+               i++;
+       }
+       list_for_each_entry(user, &target->target_in_accounts, ulist) {
+               kern_attrs[i].mode = 0600;
+               strlcpy(kern_attrs[i].name, user->sysfs_name,
+                       sizeof(kern_attrs[i].name));
+               i++;
+       }
+       list_for_each_entry(user, &target->target_out_accounts, ulist) {
+               kern_attrs[i].mode = 0600;
+               strlcpy(kern_attrs[i].name, user->sysfs_name,
+                       sizeof(kern_attrs[i].name));
+               i++;
+       }
+
+       log_debug(1, "Adding target %s (attrs_num %d)", target->name,
+               info.attrs_num);
 
-       memcpy(info.name, name, sizeof(info.name) - 1);
-       info.tid = *tid;
        if ((err = ioctl(ctrl_fd, ADD_TARGET, &info)) < 0) {
                err = -errno;
-               log_error("Can't create target %u: %s\n", *tid,
+               log_error("Can't create target %s: %s\n", target->name,
                        strerror(errno));
-       } else
-               *tid = info.tid;
+       } else {
+               target->tid = err;
+               if (tid != NULL)
+                       *tid = err;
+               err = 0;
+       }
 
+       free(kern_attrs);
+
+out:
        return err;
 }
 
-int kernel_target_destroy(u32 tid)
+int kernel_target_destroy(u32 tid, u32 cookie)
 {
        struct iscsi_kern_target_info info;
        int res;
 
        memset(&info, 0, sizeof(info));
        info.tid = tid;
+       info.cookie = cookie;
 
        res = ioctl(ctrl_fd, DEL_TARGET, &info);
        if (res < 0) {
@@ -140,6 +208,88 @@ int kernel_target_destroy(u32 tid)
        return res;
 }
 
+#ifndef CONFIG_SCST_PROC
+
+int kernel_attr_add(struct target *target, const char *name, u32 mode,
+       u32 cookie)
+{
+       struct iscsi_kern_attr_info info;
+       int res;
+
+       memset(&info, 0, sizeof(info));
+       if (target != NULL)
+               info.tid = target->tid;
+       info.cookie = cookie;
+
+       info.attr.mode = mode;
+       strlcpy(info.attr.name, name, sizeof(info.attr.name));
+
+       res = ioctl(ctrl_fd, ISCSI_ATTR_ADD, &info);
+       if (res < 0)
+               res = -errno;
+
+       return res;
+}
+
+int kernel_attr_del(struct target *target, const char *name, u32 cookie)
+{
+       struct iscsi_kern_attr_info info;
+       int res;
+
+       memset(&info, 0, sizeof(info));
+       if (target != NULL)
+               info.tid = target->tid;
+       info.cookie = cookie;
+
+       strlcpy(info.attr.name, name, sizeof(info.attr.name));
+
+       res = ioctl(ctrl_fd, ISCSI_ATTR_DEL, &info);
+       if (res < 0)
+               res = -errno;
+
+       return res;
+}
+
+static int __kernel_user_cmd(struct iscsi_user *user, u32 cookie, unsigned int cmd)
+{
+       struct iscsi_kern_attr_info info;
+       int res;
+
+       memset(&info, 0, sizeof(info));
+       if (user->target != NULL)
+               info.tid = user->target->tid;
+       info.cookie = cookie;
+
+       info.attr.mode = 0600;
+       strlcpy(info.attr.name, user->sysfs_name, sizeof(info.attr.name));
+
+       res = ioctl(ctrl_fd, cmd, &info);
+       if (res < 0)
+               res = -errno;
+
+       return res;
+}
+
+int kernel_user_add(struct iscsi_user *user, u32 cookie)
+{
+       int res;
+       res = __kernel_user_cmd(user, cookie, ISCSI_ATTR_ADD);
+       if (res != 0)
+               log_error("Can't add user %s (%d)\n", user->name, res);
+       return res;
+}
+
+int kernel_user_del(struct iscsi_user *user, u32 cookie)
+{
+       int res;
+       res = __kernel_user_cmd(user, cookie, ISCSI_ATTR_DEL);
+       if (res != 0)
+               log_error("Can't del user %s (%d)\n", user->name, res);
+       return res;
+}
+
+#endif /* CONFIG_SCST_PROC */
+
 int kernel_conn_destroy(u32 tid, u64 sid, u32 cid)
 {
        int err;
@@ -158,82 +308,117 @@ int kernel_conn_destroy(u32 tid, u64 sid, u32 cid)
        return err;
 }
 
-int kernel_param_get(u32 tid, u64 sid, int type, struct iscsi_param *param)
+int kernel_params_get(u32 tid, u64 sid, int type, struct iscsi_param *params)
 {
        int err, i;
-       struct iscsi_kern_param_info info;
+       struct iscsi_kern_params_info info;
+
+       if (sid == 0) {
+               log_error("kernel_params_get(): sid must be not %d", 0);
+               err = -EINVAL;
+               goto out;
+       }
 
        memset(&info, 0, sizeof(info));
        info.tid = tid;
        info.sid = sid;
-       info.param_type = type;
+       info.params_type = type;
 
        if ((err = ioctl(ctrl_fd, ISCSI_PARAM_GET, &info)) < 0) {
                err = -errno;
-               log_debug(1, "Can't get session param for session 0x%" PRIu64 
+               log_debug(1, "Can't get session params for session 0x%" PRIu64 
                        " (tid %u, err %d): %s\n", sid, tid, err, strerror(errno));
        }
 
        if (type == key_session)
                for (i = 0; i < session_key_last; i++)
-                       param[i].val = info.session_param[i];
+                       params[i].val = info.session_params[i];
        else
                for (i = 0; i < target_key_last; i++)
-                       param[i].val = info.target_param[i];
+                       params[i].val = info.target_params[i];
 
+out:
        return err;
 }
 
-int kernel_param_set(u32 tid, u64 sid, int type, u32 partial,
-       struct iscsi_param *param)
+int kernel_params_set(u32 tid, u64 sid, int type, u32 partial,
+       const struct iscsi_param *params)
 {
        int i, err;
-       struct iscsi_kern_param_info info;
+       struct iscsi_kern_params_info info;
+
+       if (sid == 0) {
+               log_error("kernel_params_set(): sid must be not %d", 0);
+               err = -EINVAL;
+               goto out;
+       }
 
        memset(&info, 0, sizeof(info));
        info.tid = tid;
        info.sid = sid;
-       info.param_type = type;
+       info.params_type = type;
        info.partial = partial;
 
-       if (info.param_type == key_session)
+       if (info.params_type == key_session)
                for (i = 0; i < session_key_last; i++)
-                       info.session_param[i] = param[i].val;
+                       info.session_params[i] = params[i].val;
        else
                for (i = 0; i < target_key_last; i++)
-                       info.target_param[i] = param[i].val;
+                       info.target_params[i] = params[i].val;
 
        if ((err = ioctl(ctrl_fd, ISCSI_PARAM_SET, &info)) < 0) {
                err = -errno;
-               log_error("Can't set session param for session 0x%" PRIu64 
+               log_error("Can't set session params for session 0x%" PRIu64 
                        " (tid %u, type %d, partial %d, err %d): %s\n", sid,
                        tid, type, partial, err, strerror(errno));
        }
 
+out:
        return err;
 }
 
-int kernel_session_create(u32 tid, u64 sid, u32 exp_cmd_sn,
-       char *name, char *user)
+int kernel_session_create(struct connection *conn)
 {
        struct iscsi_kern_session_info info;
-       int res;
+       int res, i;
+       struct target *target;
 
        memset(&info, 0, sizeof(info));
 
-       info.tid = tid;
-       info.sid = sid;
-       info.exp_cmd_sn = exp_cmd_sn;
-       strncpy(info.initiator_name, name, sizeof(info.initiator_name) - 1);
-       strncpy(info.user_name, user, sizeof(info.user_name) - 1);
+       info.tid = conn->tid;
+       info.sid = conn->sess->sid.id64;
+       info.exp_cmd_sn = conn->exp_cmd_sn;
+       strlcpy(info.initiator_name, conn->sess->initiator, sizeof(info.initiator_name));
+
+#ifdef CONFIG_SCST_PROC
+       if (conn->user != NULL)
+               strlcpy(info.user_name, conn->user, sizeof(info.user_name));
+       else
+               info.user_name[0] = '\0';
+#endif
+
+       target = target_find_by_id(conn->tid);
+       if (target == NULL) {
+               log_error("Target %d not found", conn->tid);
+               res = -EINVAL;
+               goto out;
+       }
+
+       for (i = 0; i < session_key_last; i++)
+               info.session_params[i] = conn->session_params[i].val;
+
+       for (i = 0; i < target_key_last; i++)
+               info.target_params[i] = target->target_params[i];
 
        res = ioctl(ctrl_fd, ADD_SESSION, &info);
        if (res < 0) {
                res = -errno;
                log_error("Can't create sess 0x%" PRIu64 " (tid %d, "
-                       "initiator %s): %s\n", sid, tid, name, strerror(errno));
+                       "initiator %s): %s\n", conn->sess->sid.id64, conn->tid,
+                       conn->sess->initiator, strerror(errno));
        }
 
+out:
        return res;
 }
 
index 6a23bf3..10dc387 100644 (file)
 #include <string.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
+#include <ctype.h>
 
 #include <asm/types.h>
 #include <sys/socket.h>
 #include <linux/netlink.h>
 
+#include <scst_const.h>
+
 #include "iscsid.h"
 
+#define ISCSI_ISNS_SYSFS_ACCESS_CONTROL_ENABLED        "AccessControl"
+
 static struct sockaddr_nl src_addr, dest_addr;
 
 static int nl_write(int fd, void *data, int len)
@@ -63,6 +68,7 @@ static int nl_read(int fd, void *data, int len)
        struct iovec iov[2];
        struct msghdr msg;
        struct nlmsghdr nlh;
+       int res;
 
        iov[0].iov_base = &nlh;
        iov[0].iov_len = sizeof(nlh);
@@ -75,14 +81,643 @@ static int nl_read(int fd, void *data, int len)
        msg.msg_iov = iov;
        msg.msg_iovlen = 2;
 
-       return recvmsg(fd, &msg, MSG_DONTWAIT);
+       res = recvmsg(fd, &msg, iscsi_enabled ? MSG_DONTWAIT : 0);
+       if (res > 0) {
+               res -= sizeof(nlh);
+               if (res < 0)
+                       res = -EPIPE;
+       }
+
+       return res;
+}
+
+#ifndef CONFIG_SCST_PROC
+
+static int send_mgmt_cmd_res(u32 tid, u32 cookie, u32 req_cmd, int result,
+       const char *res_str)
+{
+       struct iscsi_kern_mgmt_cmd_res_info cinfo;
+       int res;
+
+       memset(&cinfo, 0, sizeof(cinfo));
+       cinfo.tid = tid;
+       cinfo.cookie = cookie;
+       cinfo.req_cmd = req_cmd;
+       cinfo.result = result;
+
+       if (res_str != NULL)
+               strlcpy(cinfo.value, res_str, sizeof(cinfo.value));
+
+       log_debug(1, "Sending result %d (cookie %d)", result, cookie);
+
+       res = ioctl(ctrl_fd, MGMT_CMD_CALLBACK, &cinfo);
+       if (res != 0) {
+               log_error("Can't send mgmt reply (cookie %d, result %d, "
+                       "res %d): %s\n", cookie, result, res, strerror(errno));
+       }
+
+       return res;
+}
+
+static int handle_e_add_target(int fd, const struct iscsi_kern_event *event)
+{
+       int res, rc;
+       char *buf;
+       int size, offs;
+
+       if (event->param1_size == 0) {
+               log_error("Incorrect E_ADD_TARGET: %s", "Target name expected");
+               res = -EINVAL;
+               goto out;
+       }
+
+       /* Params are not 0-terminated */
+
+       size = strlen("Target ") + event->param1_size + 2 + event->param2_size + 1;
+
+       buf = malloc(size);
+       if (buf == NULL) {
+               log_error("Unable to allocate tmp buffer (size %d)", size);
+               res = -ENOMEM;
+               goto out;
+       }
+
+       offs = sprintf(buf, "Target ");
+
+       while (1) {
+               if ((rc = nl_read(fd, &buf[offs], event->param1_size)) < 0) {
+                       if ((errno == EINTR) || (errno == EAGAIN))
+                               continue;
+                       log_error("read netlink fd (%d) failed: %s", fd,
+                               strerror(errno));
+                       send_mgmt_cmd_res(0, event->cookie, E_ADD_TARGET, -errno, NULL);
+                       exit(1);
+               }
+               break;
+       }
+
+       offs += rc;
+       offs += sprintf(&buf[offs], "; ");
+
+       if (event->param2_size > 0) {
+               while (1) {
+                       if ((rc = nl_read(fd, &buf[offs], event->param2_size)) < 0) {
+                               if ((errno == EINTR) || (errno == EAGAIN))
+                                       continue;
+                               log_error("read netlink fd (%d) failed: %s", fd,
+                                       strerror(errno));
+                               send_mgmt_cmd_res(0, event->cookie, E_ADD_TARGET, -errno, NULL);
+                               exit(1);
+                       }
+                       break;
+               }
+               offs += rc;
+       }
+
+       buf[offs] = '\0';
+
+       log_debug(1, "Going to parse %s", buf);
+
+       res = config_parse_main(buf, event->cookie);
+       if (res != 0)
+               goto out_free;
+
+out_free:
+       free(buf);
+
+out:
+       return res;
+}
+
+static int handle_e_del_target(int fd, const struct iscsi_kern_event *event)
+{
+       int res;
+
+       log_debug(2, "Going to delete target %d", event->tid);
+
+       res = target_del(event->tid, event->cookie);
+       return res;
+}
+
+static int handle_add_user(struct target *target, int dir, char *sysfs_name,
+       char *p, u32 cookie)
+{
+       int res;
+       char *name, *pass;
+
+       name = config_sep_string(&p);
+       pass = config_sep_string(&p);
+
+       res = __config_account_add(target, dir, name, pass, sysfs_name, 1, cookie);
+
+       return res;
+}
+
+static int handle_add_param(struct target *target, char *p, u32 cookie)
+{
+       int res, dir;
+       char *pp;
+
+       pp = config_sep_string(&p);
+       dir = params_index_by_name_numwild(pp, user_keys);
+       if (dir >= 0)
+               res = handle_add_user(target, dir, pp, p, cookie);
+       else {
+               log_error("Syntax error at %s", pp);
+               res = -EINVAL;
+               goto out;
+       }
+
+out:
+       return res;
+}
+
+static int handle_del_user(struct target *target, int dir, char *p, u32 cookie)
+{
+       int res;
+       char *name;
+
+       name = config_sep_string(&p);
+
+       res = config_account_del((target != NULL) ? target->tid : 0, dir,
+                       name, cookie);
+
+       return res;
+}
+
+static int handle_del_param(struct target *target, char *p, u32 cookie)
+{
+       int res, dir;
+       char *pp;
+
+       pp = config_sep_string(&p);
+       dir = params_index_by_name_numwild(pp, user_keys);
+       if (dir >= 0)
+               res = handle_del_user(target, dir, p, cookie);
+       else {
+               log_error("Syntax error at %s", pp);
+               res = -EINVAL;
+               goto out;
+       }
+
+out:
+       return res;
+}
+
+static int handle_e_mgmt_cmd(int fd, const struct iscsi_kern_event *event)
+{
+       int res, rc;
+       char *buf, *p, *pp;
+       int size;
+
+       if (event->param1_size == 0) {
+               log_error("Incorrect E_MGMT_CMD: %s", "command expected");
+               res = -EINVAL;
+               goto out;
+       }
+
+       /* Params are not 0-terminated */
+
+       size = event->param1_size + 1;
+
+       buf = malloc(size);
+       if (buf == NULL) {
+               log_error("Unable to allocate tmp buffer (size %d)", size);
+               res = -ENOMEM;
+               goto out;
+       }
+
+       while (1) {
+               if ((rc = nl_read(fd, buf, event->param1_size)) < 0) {
+                       if ((errno == EINTR) || (errno == EAGAIN))
+                               continue;
+                       log_error("read netlink fd (%d) failed: %s", fd,
+                               strerror(errno));
+                       send_mgmt_cmd_res(0, event->cookie, E_MGMT_CMD, -errno, NULL);
+                       exit(1);
+               }
+               break;
+       }
+
+       buf[rc] = '\0';
+
+       log_debug(1, "Going to parse %s", buf);
+
+       p = buf;
+       pp = config_sep_string(&p);
+       if (strcasecmp("add_attribute", pp) == 0) {
+               res = handle_add_param(NULL, p, event->cookie);
+       } else if (strcasecmp("add_target_attribute", pp) == 0) {
+               struct target *target;
+               pp = config_sep_string(&p);
+               target = target_find_by_name(pp);
+               if (target == NULL) {
+                       log_error("Target %s not found", pp);
+                       res = -ENOENT;
+                       goto out_free;
+               }
+               res = handle_add_param(target, p, event->cookie);
+       } else if (strcasecmp("del_attribute", pp) == 0) {
+               res = handle_del_param(NULL, p, event->cookie);
+       } else if (strcasecmp("del_target_parameter", pp) == 0) {
+               struct target *target;
+               pp = config_sep_string(&p);
+               target = target_find_by_name(pp);
+               if (target == NULL) {
+                       log_error("Target %s not found", pp);
+                       res = -ENOENT;
+                       goto out_free;
+               }
+               res = handle_del_param(target, p, event->cookie);
+       } else {
+               log_error("Syntax error at %s", p);
+               res = -EINVAL;
+       }
+
+out_free:
+       free(buf);
+
+out:
+       return res;
+}
+
+static void add_key_mark(char *res_str, int res_str_len, int new_line)
+{
+       int offs = strlen(res_str);
+       snprintf(&res_str[offs], res_str_len - offs, "%s%s\n",
+               new_line ? "\n" : "", SCST_SYSFS_KEY_MARK);
+       return;
+}
+
+static int handle_e_get_attr_value(int fd, const struct iscsi_kern_event *event)
+{
+       int res = 0, rc, idx;
+       char *buf, *p, *pp;
+       int size;
+       struct target *target;
+       char res_str[ISCSI_MAX_ATTR_VALUE_LEN];
+
+       memset(res_str, 0, sizeof(res_str));
+
+       if (event->param1_size == 0) {
+               log_error("Incorrect E_GET_ATTR_VALUE: %s", "attr name expected");
+               res = -EINVAL;
+               goto out;
+       }
+
+       /* Params are not 0-terminated */
+
+       size = event->param1_size + 1;
+
+       buf = malloc(size);
+       if (buf == NULL) {
+               log_error("Unable to allocate tmp buffer (size %d)", size);
+               res = -ENOMEM;
+               goto out;
+       }
+
+       while (1) {
+               if ((rc = nl_read(fd, buf, event->param1_size)) < 0) {
+                       if ((errno == EINTR) || (errno == EAGAIN))
+                               continue;
+                       log_error("read netlink fd (%d) failed: %s", fd,
+                               strerror(errno));
+                       send_mgmt_cmd_res(0, event->cookie, E_GET_ATTR_VALUE, -errno, NULL);
+                       exit(1);
+               }
+               break;
+       }
+
+       buf[rc] = '\0';
+
+       log_debug(1, "Going to parse name %s", buf);
+
+       target = target_find_by_id(event->tid);
+
+       p = buf;
+       pp = config_sep_string(&p);
+       if (!((idx = params_index_by_name(pp, target_keys)) < 0)) {
+               if (target == NULL) {
+                       log_error("Target expected for attr %s", pp);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               params_val_to_str(target_keys, idx, target->target_params[idx],
+                       res_str, sizeof(res_str));
+
+               if (target->target_params[idx] != target_keys[idx].local_def)
+                       add_key_mark(res_str, sizeof(res_str), 1);
+       } else if (!((idx = params_index_by_name(pp, session_keys)) < 0)) {
+               if (target == NULL) {
+                       log_error("Target expected for attr %s", pp);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               params_val_to_str(session_keys, idx, target->session_params[idx],
+                       res_str, sizeof(res_str));
+
+               if (target->session_params[idx] != session_keys[idx].local_def)
+                       add_key_mark(res_str, sizeof(res_str), 1);
+       } else if (!((idx = params_index_by_name_numwild(pp, user_keys)) < 0)) {
+               struct iscsi_user *user;
+
+               user = account_lookup_by_sysfs_name(target, idx, pp);
+               if (user == NULL) {
+                       log_error("Unknown user attribute %s", pp);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               snprintf(res_str, sizeof(res_str), "%s %s\n", user->name,
+                       user->password);
+               add_key_mark(res_str, sizeof(res_str), 0);
+       } else if (strcasecmp(ISCSI_ENABLED_ATTR_NAME, pp) == 0) {
+               if (target != NULL) {
+                       log_error("Not NULL target %s for global attribute %s",
+                               target->name, pp);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               snprintf(res_str, sizeof(res_str), "%d\n", iscsi_enabled);
+       } else if (strcasecmp(ISCSI_ISNS_SERVER_PARAM_NAME, pp) == 0) {
+               if (target != NULL) {
+                       log_error("Not NULL target %s for global attribute %s",
+                               target->name, pp);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               if (isns_server != NULL) {
+                       snprintf(res_str, sizeof(res_str), "%s %s\n", isns_server,
+                               isns_access_control ? ISCSI_ISNS_SYSFS_ACCESS_CONTROL_ENABLED : "");
+                       add_key_mark(res_str, sizeof(res_str), 0);
+               } else
+                       snprintf(res_str, sizeof(res_str), "%s\n", "");
+       } else {
+               log_error("Unknown attribute %s", pp);
+               res = -EINVAL;
+               goto out_free;
+       }
+
+       send_mgmt_cmd_res(event->tid, event->cookie, E_GET_ATTR_VALUE, 0, res_str);
+
+out_free:
+       free(buf);
+
+out:
+       return res;
 }
 
+static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event)
+{
+       int res = 0, rc, idx;
+       char *buf, *p, *pp, *n;
+       struct target *target;
+       int size, offs;
+       u32 val;
+
+       if (event->param1_size == 0) {
+               log_error("Incorrect E_SET_ATTR_VALUE: %s", "attr name expected");
+               res = -EINVAL;
+               goto out;
+       }
+
+       if (event->param2_size == 0) {
+               log_error("Incorrect E_SET_ATTR_VALUE: %s", "attr value expected");
+               res = -EINVAL;
+               goto out;
+       }
+
+       /* Params are not 0-terminated */
+       size = event->param1_size + 1 + 1 + event->param2_size + 1;
+
+       buf = malloc(size);
+       if (buf == NULL) {
+               log_error("Unable to allocate tmp buffer (size %d)", size);
+               res = -ENOMEM;
+               goto out;
+       }
+
+       while (1) {
+               if ((rc = nl_read(fd, buf, event->param1_size)) < 0) {
+                       if ((errno == EINTR) || (errno == EAGAIN))
+                               continue;
+                       log_error("read netlink fd (%d) failed: %s", fd,
+                               strerror(errno));
+                       send_mgmt_cmd_res(0, event->cookie, E_SET_ATTR_VALUE, -errno, NULL);
+                       exit(1);
+               }
+               break;
+       }
+
+       offs = rc;
+       offs += sprintf(&buf[offs], " ");
+
+       while (1) {
+               if ((rc = nl_read(fd, &buf[offs], event->param2_size)) < 0) {
+                       if ((errno == EINTR) || (errno == EAGAIN))
+                               continue;
+                       log_error("read netlink fd (%d) failed: %s", fd,
+                               strerror(errno));
+                       send_mgmt_cmd_res(0, event->cookie, E_SET_ATTR_VALUE, -errno, NULL);
+                       exit(1);
+               }
+               break;
+       }
+
+       offs += rc;
+       buf[offs] = '\0';
+
+       log_debug(1, "Going to parse %s", buf);
+
+       target = target_find_by_id(event->tid);
+
+       p = buf;
+       pp = config_sep_string(&p);
+       if (!((idx = params_index_by_name(pp, target_keys)) < 0)) {
+               if (target == NULL) {
+                       log_error("Target expected for attr %s", pp);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               pp = config_sep_string(&p);
+
+               n = config_sep_string(&p);
+               if (*n != '\0') {
+                       log_error("Unexpected parameter value %s\n", n);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               res = params_str_to_val(target_keys, idx, pp, &val);
+               if (res < 0) {
+                       log_error("Wrong value %s for parameter %s\n",
+                               pp, target_keys[idx].name);
+                       goto out_free;
+               }
+
+               res = params_check_val(target_keys, idx, &val);
+               if (res < 0) {
+                       log_error("Wrong value %u for parameter %s\n",
+                               val, target_keys[idx].name);
+                       goto out_free;
+               }
+
+               target->target_params[idx] = val;
+       } else if (!((idx = params_index_by_name(pp, session_keys)) < 0)) {
+               if (target == NULL) {
+                       log_error("Target expected for attr %s", pp);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               pp = config_sep_string(&p);
+
+               n = config_sep_string(&p);
+               if (*n != '\0') {
+                       log_error("Unexpected parameter value %s\n", n);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               res = params_str_to_val(session_keys, idx, pp, &val);
+               if (res < 0) {
+                       log_error("Wrong value %s for parameter %s\n",
+                               pp, session_keys[idx].name);
+                       goto out_free;
+               }
+
+               res = params_check_val(session_keys, idx, &val);
+               if (res < 0) {
+                       log_error("Wrong value %u for parameter %s\n",
+                               val, session_keys[idx].name);
+                       goto out_free;
+               }
+
+               target->session_params[idx] = val;
+       } else if (!((idx = params_index_by_name_numwild(pp, user_keys)) < 0)) {
+               struct iscsi_user *user;
+
+               user = account_lookup_by_sysfs_name(target, idx, pp);
+               if (user == NULL) {
+                       log_error("Unknown user attribute %s", pp);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               res = account_replace(target, idx, pp, p);
+               if (res != 0)
+                       goto out_free;
+       } else if (strcasecmp(ISCSI_ENABLED_ATTR_NAME, pp) == 0) {
+               if (target != NULL) {
+                       log_error("Not NULL target %s for global attribute %s",
+                               target->name, pp);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               pp = config_sep_string(&p);
+               if (strcmp(pp, "1") == 0)
+                       iscsi_enabled = 1;
+               else if (strcmp(pp, "0") == 0)
+                       iscsi_enabled = 0;
+               else {
+                       log_error("Unknown value %s", pp);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+       } else if (strcasecmp(ISCSI_ISNS_SERVER_PARAM_NAME, pp) == 0) {
+               if (target != NULL) {
+                       log_error("Not NULL target %s for global attribute %s",
+                               target->name, pp);
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               if (isns_server != NULL)
+                       isns_exit();
+
+               pp = config_sep_string(&p);
+               if (*pp == '\0') {
+                       goto done;
+               }
+
+               isns_access_control = 0;
+               isns_server = strdup(pp);
+               if (isns_server == NULL) {
+                       log_error("Unable to dublicate iSNS server name %s", pp);
+                       res = -ENOMEM;
+                       goto out_free;
+               }
+
+               pp = config_sep_string(&p);
+               if (strcasecmp(ISCSI_ISNS_SYSFS_ACCESS_CONTROL_ENABLED, pp) == 0) {
+                       pp = config_sep_string(&p);
+                       if (strcasecmp(pp, "No") == 0)
+                               isns_access_control = 0;
+                       else
+                               isns_access_control = 1;
+               } else if (*pp != '\0') {
+                       log_error("Unknown parameter %s", pp);
+                       res = -EINVAL;
+                       goto out_free_server;
+               }
+
+               res = isns_init();
+               if (res == 0) {
+                       struct target *t;
+                       int rc;
+
+                       list_for_each_entry(t, &targets_list, tlist) {
+                               rc = isns_target_register(t->name);
+                               if (rc < 0) {
+                                       /*
+                                        * iSNS server can be temporary not
+                                        * available.
+                                        */
+                                       goto out_free_isns_exit;
+                               }
+                       }
+               } else
+                       goto out_free_server;
+       } else {
+               log_error("Unknown attribute %s", pp);
+               res = -EINVAL;
+               goto out_free;
+       }
+
+done:
+       send_mgmt_cmd_res(event->tid, event->cookie, E_SET_ATTR_VALUE, 0, NULL);
+
+out_free:
+       free(buf);
+
+out:
+       return res;
+
+out_free_isns_exit:
+       isns_exit();
+
+out_free_server:
+       free(isns_server);
+       isns_server = NULL;
+       goto out;
+}
+
+#endif /* CONFIG_SCST_PROC */
+
 void handle_iscsi_events(int fd)
 {
        struct session *session;
        struct connection *conn;
        struct iscsi_kern_event event;
+#ifndef CONFIG_SCST_PROC
+       struct target *target;
+#endif
        int rc;
 
 retry:
@@ -91,63 +726,77 @@ retry:
                        return;
                if (errno == EINTR)
                        goto retry;
-               log_error("read netlink fd (%d)", errno);
+               log_error("read netlink fd (%d) failed: %s", fd, strerror(errno));
                exit(1);
        }
 
-       log_debug(1, "conn %u session %#" PRIx64 " target %u, code %u",
-                 event.cid, event.sid, event.tid, event.code);
+       log_debug(1, "target %u, session %#" PRIx64 ", conn %u, code %u, cookie %d",
+                 event.tid, event.sid, event.cid, event.code, event.cookie);
+
+       /*
+        * Let's always report errors through send_mgmt_cmd_res(). If the error
+        * was returned by the corresponding ioctl(), it will lead to blank
+        * MGMT_CMD_CALLBACK ioctl()'s, but that's OK, because kernel will
+        * not reuse the cookie. Better to have extra return call, than no call
+        * at all.
+        */
 
        switch (event.code) {
-       case E_ENABLE_TARGET:
-       {
-               struct target *target;
-               struct iscsi_kern_target_info info;
+#ifndef CONFIG_SCST_PROC
+       case E_ADD_TARGET:
+               rc = handle_e_add_target(fd, &event);
+               if (rc != 0)
+                       send_mgmt_cmd_res(event.tid, event.cookie, E_ADD_TARGET, rc, NULL);
+               break;
+
+       case E_DEL_TARGET:
+               rc = handle_e_del_target(fd, &event);
+               if (rc != 0)
+                       send_mgmt_cmd_res(event.tid, event.cookie, E_DEL_TARGET, rc, NULL);
+               break;
+
+       case E_MGMT_CMD:
+               rc = handle_e_mgmt_cmd(fd, &event);
+               if (rc != 0)
+                       send_mgmt_cmd_res(event.tid, event.cookie, E_MGMT_CMD, rc, NULL);
+               break;
 
+       case E_ENABLE_TARGET:
                target = target_find_by_id(event.tid);
                if (target == NULL) {
                        log_error("Target %d not found", event.tid);
-                       goto out;
-               }
-
-               target->tgt_enabled = 1;
-
-               memset(&info, 0, sizeof(info));
-
-               info.tid = event.tid;
-               rc = ioctl(ctrl_fd, ENABLE_TARGET, &info);
-               if (rc < 0) {
-                       log_error("Can't enable target %u: %s\n", event.tid,
-                               strerror(errno));
-                       goto out;
-               }
+                       rc = -ENOENT;
+               } else
+                       rc = 0;
+               rc |= send_mgmt_cmd_res(event.tid, event.cookie, E_ENABLE_TARGET, rc, NULL);
+               if (rc == 0)
+                       target->tgt_enabled = 1;
                break;
-       }
 
        case E_DISABLE_TARGET:
-       {
-               struct target *target;
-               struct iscsi_kern_target_info info;
-
                target = target_find_by_id(event.tid);
                if (target == NULL) {
                        log_error("Target %d not found", event.tid);
-                       goto out;
-               }
-
-               target->tgt_enabled = 0;
+                       rc = -ENOENT;
+               } else
+                       rc = 0;
+               rc |= send_mgmt_cmd_res(event.tid, event.cookie, E_DISABLE_TARGET, rc, NULL);
+               if (rc == 0)
+                       target->tgt_enabled = 0;
+               break;
 
-               memset(&info, 0, sizeof(info));
+       case E_GET_ATTR_VALUE:
+               rc = handle_e_get_attr_value(fd, &event);
+               if (rc != 0)
+                       send_mgmt_cmd_res(event.tid, event.cookie, E_GET_ATTR_VALUE, rc, NULL);
+               break;
 
-               info.tid = event.tid;
-               rc = ioctl(ctrl_fd, DISABLE_TARGET, &info);
-               if (rc < 0) {
-                       log_error("Can't disable target %u: %s\n", event.tid,
-                               strerror(errno));
-                       goto out;
-               }
+       case E_SET_ATTR_VALUE:
+               rc = handle_e_set_attr_value(fd, &event);
+               if (rc != 0)
+                       send_mgmt_cmd_res(event.tid, event.cookie, E_SET_ATTR_VALUE, rc, NULL);
                break;
-       }
+#endif /* CONFIG_SCST_PROC */
 
        case E_CONN_CLOSE:
                session = session_find_id(event.tid, event.sid);
@@ -175,7 +824,6 @@ retry:
                break;
        }
 
-out:
        return;
 }
 
index bcaca80..1de8764 100644 (file)
@@ -245,9 +245,9 @@ out_close:
        return err;
 }
 
-static void show_iscsi_param(int type, struct iscsi_param *param)
+static void show_iscsi_params(int type, struct iscsi_param *param)
 {
-       int i, nr;
+       int i, nr, len;
        char buf[1024], *p;
        struct iscsi_key *keys;
 
@@ -261,10 +261,11 @@ static void show_iscsi_param(int type, struct iscsi_param *param)
 
        for (i = 0; i < nr; i++) {
                memset(buf, 0, sizeof(buf));
-               strcpy(buf, keys[i].name);
-               p = buf + strlen(buf);
+               strlcpy(buf, keys[i].name, sizeof(buf));
+               len = strlen(buf);
+               p = buf + len;
                *p++ = '=';
-               param_val_to_str(keys, i, param[i].val, p);
+               params_val_to_str(keys, i, param[i].val, p, sizeof(buf) - (len + 1));
                printf("%s\n", buf);
        }
 }
@@ -282,31 +283,31 @@ static int parse_trgt_params(struct msg_trgt *msg, char *params)
                        continue;
                *q++ = '\0';
 
-               if (!((idx = param_index_by_name(p, target_keys)) < 0)) {
-                       if (param_str_to_val(target_keys, idx, q, &val)) {
+               if (!((idx = params_index_by_name(p, target_keys)) < 0)) {
+                       if (params_str_to_val(target_keys, idx, q, &val)) {
                                fprintf(stderr,
                                        "Invalid %s value \"%s\".\n",
                                        target_keys[idx].name, q);
                                return -EINVAL;
                        }
-                       if (!param_check_val(target_keys, idx, &val))
+                       if (!params_check_val(target_keys, idx, &val))
                                msg->target_partial |= (1 << idx);
-                       msg->target_param[idx].val = val;
+                       msg->target_params[idx].val = val;
                        msg->type |= 1 << key_target;
 
                        continue;
                }
 
-               if (!((idx = param_index_by_name(p, session_keys)) < 0)) {
-                       if (param_str_to_val(session_keys, idx, q, &val)) {
+               if (!((idx = params_index_by_name(p, session_keys)) < 0)) {
+                       if (params_str_to_val(session_keys, idx, q, &val)) {
                                fprintf(stderr,
                                        "Invalid %s value \"%s\".\n",
                                        session_keys[idx].name, q);
                                return -EINVAL;
                        }
-                       if (!param_check_val(session_keys, idx, &val))
+                       if (!params_check_val(session_keys, idx, &val))
                                msg->session_partial |= (1 << idx);
-                       msg->session_param[idx].val = val;
+                       msg->session_params[idx].val = val;
                        msg->type |= 1 << key_session;
 
                        continue;
@@ -346,7 +347,7 @@ static int trgt_handle(int op, u32 set, u32 tid, char *params)
                        goto out;
                }
                req.rcmnd = C_TRGT_NEW;
-               strncpy(req.u.trgt.name, p, sizeof(req.u.trgt.name) - 1);
+               strlcpy(req.u.trgt.name, p, sizeof(req.u.trgt.name));
                break;
        }
        case OP_DELETE:
@@ -364,7 +365,7 @@ static int trgt_handle(int op, u32 set, u32 tid, char *params)
 
        err = iscsid_request(&req, NULL, 0);
        if (!err && req.rcmnd == C_TRGT_SHOW)
-               show_iscsi_param(key_target, req.u.trgt.target_param);
+               show_iscsi_params(key_target, req.u.trgt.target_params);
 
 out:
        return err;
@@ -395,7 +396,7 @@ static int sess_handle(int op, u32 set, u32 tid, u64 sid, char *params)
                req.rcmnd = C_SESS_SHOW;
                err = iscsid_request(&req, NULL, 0);
                if (!err)
-                       show_iscsi_param(key_session, req.u.trgt.session_param);
+                       show_iscsi_params(key_session, req.u.trgt.session_params);
                break;
        }
 
@@ -424,14 +425,14 @@ static int parse_user_params(char *params, u32 *auth_dir, char **user,
                                        "Already specified IncomingUser %s\n",
                                        q);
                        *user = q;
-                       *auth_dir = AUTH_DIR_INCOMING;
+                       *auth_dir = ISCSI_USER_DIR_INCOMING;
                } else if (!strcasecmp(p, "OutgoingUser")) {
                        if (*user)
                                fprintf(stderr,
                                        "Already specified OutgoingUser %s\n",
                                        q);
                        *user = q;
-                       *auth_dir = AUTH_DIR_OUTGOING;
+                       *auth_dir = ISCSI_USER_DIR_OUTGOING;
                } else if (!strcasecmp(p, "Password")) {
                        if (*pass)
                                fprintf(stderr,
@@ -453,7 +454,7 @@ static void show_account(int auth_dir, char *user, char *pass)
        if (pass)
                snprintf(buf + strlen(buf), ISCSI_NAME_LEN, " %s", pass);
 
-       printf("%sUser %s\n", (auth_dir == AUTH_DIR_INCOMING) ?
+       printf("%sUser %s\n", (auth_dir == ISCSI_USER_DIR_INCOMING) ?
               "Incoming" : "Outgoing", buf);
 }
 
@@ -462,8 +463,7 @@ static int user_handle_show_user(struct iscsi_adm_req *req, char *user)
        int err;
 
        req->rcmnd = C_ACCT_SHOW;
-       strncpy(req->u.acnt.u.user.name, user,
-               sizeof(req->u.acnt.u.user.name) - 1);
+       strlcpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name));
 
        err = iscsid_request(req, NULL, 0);
        if (!err)
@@ -479,7 +479,7 @@ static int user_handle_show_list(struct iscsi_adm_req *req)
        size_t buf_sz = 0;
        char *buf;
 
-       req->u.acnt.auth_dir = AUTH_DIR_INCOMING;
+       req->u.acnt.auth_dir = ISCSI_USER_DIR_INCOMING;
        req->rcmnd = C_ACCT_LIST;
 
        do {
@@ -513,8 +513,8 @@ static int user_handle_show_list(struct iscsi_adm_req *req)
                        show_account(req->u.acnt.auth_dir,
                                     &buf[i * ISCSI_NAME_LEN], NULL);
 
-               if (req->u.acnt.auth_dir == AUTH_DIR_INCOMING) {
-                       req->u.acnt.auth_dir = AUTH_DIR_OUTGOING;
+               if (req->u.acnt.auth_dir == ISCSI_USER_DIR_INCOMING) {
+                       req->u.acnt.auth_dir = ISCSI_USER_DIR_OUTGOING;
                        buf_sz = 0;
                        retry = 1;
                }
@@ -546,10 +546,8 @@ static int user_handle_new(struct iscsi_adm_req *req, char *user, char *pass)
 
        req->rcmnd = C_ACCT_NEW;
 
-       strncpy(req->u.acnt.u.user.name, user,
-               sizeof(req->u.acnt.u.user.name) - 1);
-       strncpy(req->u.acnt.u.user.pass, pass,
-               sizeof(req->u.acnt.u.user.pass) - 1);
+       strlcpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name));
+       strlcpy(req->u.acnt.u.user.pass, pass, sizeof(req->u.acnt.u.user.pass));
 
        return iscsid_request(req, NULL, 0);
 }
@@ -566,8 +564,7 @@ static int user_handle_del(struct iscsi_adm_req *req, char *user, char *pass)
 
        req->rcmnd = C_ACCT_DEL;
 
-       strncpy(req->u.acnt.u.user.name, user,
-               sizeof(req->u.acnt.u.user.name) - 1);
+       strlcpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name));
 
        return iscsid_request(req, NULL, 0);
 }
index fe12889..0b388a2 100644 (file)
@@ -25,8 +25,8 @@ struct msg_trgt {
        u32 type;
        u32 session_partial;
        u32 target_partial;
-       struct iscsi_param session_param[session_key_last];
-       struct iscsi_param target_param[target_key_last];
+       struct iscsi_param session_params[session_key_last];
+       struct iscsi_param target_params[target_key_last];
 };
 
 struct msg_acnt {
index ce4a80f..c7c27ab 100644 (file)
@@ -465,7 +465,7 @@ out:
        return;
 }
 
-static void event_loop(int timeout)
+static void event_loop(void)
 {
        int res, i;
 
@@ -491,9 +491,13 @@ static void event_loop(int timeout)
        close(init_report_pipe[1]);
 
        while (1) {
-               res = poll(poll_array, POLL_MAX, timeout);
+               if (!iscsi_enabled) {
+                       handle_iscsi_events(nl_fd);
+                       continue;
+               }
+               res = poll(poll_array, POLL_MAX, isns_timeout);
                if (res == 0) {
-                       isns_handle(1, &timeout);
+                       isns_handle(1);
                        continue;
                } else if (res < 0) {
                        if (errno == EINTR)
@@ -531,7 +535,7 @@ static void event_loop(int timeout)
                        iscsi_adm_request_handle(ipc_fd);
 
                if (poll_array[POLL_ISNS].revents)
-                       isns_handle(0, &timeout);
+                       isns_handle(0);
 
                if (poll_array[POLL_SCN_LISTEN].revents)
                        isns_scn_handle(1);
@@ -562,7 +566,7 @@ static void event_loop(int timeout)
        }
 }
 
-void init_max_data_seg_len(int max_data_seg_len)
+static void init_max_data_seg_len(int max_data_seg_len)
 {
        if ((session_keys[key_max_recv_data_length].local_def != -1) ||
            (session_keys[key_max_recv_data_length].max != -1) ||
@@ -597,13 +601,16 @@ void init_max_data_seg_len(int max_data_seg_len)
 
 int main(int argc, char **argv)
 {
-       int ch, longindex, timeout = -1;
+       int ch, longindex;
        char *config = NULL;
        uid_t uid = 0;
        gid_t gid = 0;
-       char *isns = NULL;
-       int isns_ac = 0;
        int max_data_seg_len = -1;
+       int err;
+
+#ifdef CONFIG_SCST_PROC
+       iscsi_enabled = 1;
+#endif
 
        if (pipe(init_report_pipe) == -1) {
                perror("pipe failed");
@@ -666,6 +673,18 @@ int main(int argc, char **argv)
 
        init_max_data_seg_len(max_data_seg_len);
 
+#ifndef CONFIG_SCST_PROC
+       err = kernel_attr_add(NULL, ISCSI_ISNS_SERVER_PARAM_NAME,
+                       S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR, 0);
+       if (err != 0)
+               exit(err);
+
+       err = kernel_attr_add(NULL, ISCSI_ENABLED_ATTR_NAME,
+                       S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR, 0);
+       if (err != 0)
+               exit(err);
+#endif
+
        if ((ipc_fd = iscsi_adm_request_listen()) < 0) {
                perror("ipc failed\n");
                exit(-1);
@@ -722,11 +741,8 @@ int main(int argc, char **argv)
                setsid();
        }
 
-       config_isns_load(config, &isns, &isns_ac);
-       if (isns)
-               timeout = isns_init(isns, isns_ac);
-
-       if (config_load(config) != 0)
+       err = config_load(config);
+       if (err != 0)
                exit(1);
 
        if (gid && setgid(gid) < 0)
@@ -735,7 +751,7 @@ int main(int argc, char **argv)
        if (uid && setuid(uid) < 0)
                perror("setuid failed");
 
-       event_loop(timeout);
+       event_loop();
 
        return 0;
 }
index a1d9e4b..f4249f9 100644 (file)
@@ -26,6 +26,8 @@
 
 #include "iscsid.h"
 
+int iscsi_enabled;
+
 static u32 ttt;
 
 static u32 get_next_ttt(struct connection *conn __attribute__((unused)))
@@ -39,7 +41,7 @@ static struct iscsi_key login_keys[] = {
        {"InitiatorAlias",},
        {"SessionType",},
        {"TargetName",},
-       {NULL, 0, 0, 0, 0, NULL},
+       {NULL,},
 };
 
 char *text_key_find(struct connection *conn, char *searchKey)
@@ -67,7 +69,7 @@ char *text_key_find(struct connection *conn, char *searchKey)
                datasize--;
 
                if (keylen == value - key - 1
-                    && !strncmp(key, searchKey, keylen))
+                    && !strncasecmp(key, searchKey, keylen))
                        return value;
        }
 }
@@ -114,7 +116,7 @@ static struct buf_segment *conn_alloc_buf_segment(struct connection *conn,
        return seg;
 }
 
-void text_key_add(struct connection *conn, char *key, char *value)
+void text_key_add(struct connection *conn, char *key, const char *value)
 {
        struct buf_segment *seg;
        int keylen = strlen(key);
@@ -126,7 +128,7 @@ void text_key_add(struct connection *conn, char *key, char *value)
        size_t data_sz;
 
        data_sz = (conn->state == STATE_FULL) ?
-               conn->session_param[key_max_xmit_data_length].val :
+               conn->session_params[key_max_xmit_data_length].val :
                INCOMING_BUFSIZE;
 
        seg = list_empty(&conn->rsp_buf_list) ? NULL :
@@ -190,14 +192,6 @@ static void text_key_add_reject(struct connection *conn, char *key)
        text_key_add(conn, key, "Reject");
 }
 
-static int account_empty(u32 tid, int dir)
-{
-       char pass[ISCSI_NAME_LEN];
-
-       memset(pass, 0, sizeof(pass));
-       return config_account_query(tid, dir, pass, pass) < 0 ? 1 : 0;
-}
-
 static void text_scan_security(struct connection *conn)
 {
        struct iscsi_login_rsp_hdr *rsp = (struct iscsi_login_rsp_hdr *)&conn->rsp.bhs;
@@ -208,7 +202,7 @@ static void text_scan_security(struct connection *conn)
        datasize = conn->req.datasize;
 
        while ((key = next_key(&data, &datasize, &value))) {
-               if (!(param_index_by_name(key, login_keys) < 0))
+               if (!(params_index_by_name(key, login_keys) < 0))
                        ;
                else if (!strcmp(key, "AuthMethod")) {
                        do {
@@ -217,13 +211,13 @@ static void text_scan_security(struct connection *conn)
                                        *nextValue++ = 0;
 
                                if (!strcmp(value, "None")) {
-                                       if (!account_empty(conn->tid, AUTH_DIR_INCOMING))
+                                       if (!accounts_empty(conn->tid, ISCSI_USER_DIR_INCOMING))
                                                continue;
                                        conn->auth_method = AUTH_NONE;
                                        text_key_add(conn, key, "None");
                                        break;
                                } else if (!strcmp(value, "CHAP")) {
-                                       if (account_empty(conn->tid, AUTH_DIR_INCOMING))
+                                       if (accounts_empty(conn->tid, ISCSI_USER_DIR_INCOMING))
                                                continue;
                                        conn->auth_method = AUTH_CHAP;
                                        text_key_add(conn, key, "CHAP");
@@ -285,7 +279,7 @@ static int login_check_reinstatement(struct connection *conn)
                                        "initiator %s)", conn->cid, conn->tid,
                                        req->sid.id64, conn->initiator);
                                conn->sess = session;
-                               insque(&conn->clist, &session->conn_list);
+                               list_add_tail(&conn->clist, &session->conn_list);
                        } else {
                                log_error("Only a single connection supported "
                                        "(initiator %s)", conn->initiator);
@@ -329,11 +323,11 @@ static void text_scan_login(struct connection *conn)
        datasize = conn->req.datasize;
 
        while ((key = next_key(&data, &datasize, &value))) {
-               if (!(param_index_by_name(key, login_keys) < 0))
+               if (!(params_index_by_name(key, login_keys) < 0))
                        ;
                else if (!strcmp(key, "AuthMethod"))
                        ;
-               else if (!((idx = param_index_by_name(key, session_keys)) < 0)) {
+               else if (!((idx = params_index_by_name(key, session_keys)) < 0)) {
                        unsigned int val;
                        char buf[32];
 
@@ -342,12 +336,12 @@ static void text_scan_login(struct connection *conn)
                                continue;
                        }
                        if (idx == key_max_recv_data_length) {
-                               conn->session_param[idx].key_state = KEY_STATE_DONE;
+                               conn->session_params[idx].key_state = KEY_STATE_DONE;
                                idx = key_max_xmit_data_length;
                        };
 
-                       if (param_str_to_val(session_keys, idx, value, &val) < 0) {
-                               if (conn->session_param[idx].key_state == KEY_STATE_START) {
+                       if (params_str_to_val(session_keys, idx, value, &val) < 0) {
+                               if (conn->session_params[idx].key_state == KEY_STATE_START) {
                                        text_key_add_reject(conn, key);
                                        continue;
                                } else {
@@ -358,30 +352,30 @@ static void text_scan_login(struct connection *conn)
                                }
                        }
 
-                       param_check_val(session_keys, idx, &val);
-                       param_set_val(session_keys, conn->session_param, idx, &val);
+                       params_check_val(session_keys, idx, &val);
+                       params_set_val(session_keys, conn->session_params, idx, &val);
 
-                       switch (conn->session_param[idx].key_state) {
+                       switch (conn->session_params[idx].key_state) {
                        case KEY_STATE_START:
                                if (iscsi_is_key_internal(idx)) {
-                                       conn->session_param[idx].key_state = KEY_STATE_DONE;
+                                       conn->session_params[idx].key_state = KEY_STATE_DONE;
                                        break;
                                }
                                memset(buf, 0, sizeof(buf));
-                               param_val_to_str(session_keys, idx, val, buf);
+                               params_val_to_str(session_keys, idx, val, buf, sizeof(buf));
                                text_key_add(conn, key, buf);
-                               conn->session_param[idx].key_state = KEY_STATE_DONE_ADDED;
+                               conn->session_params[idx].key_state = KEY_STATE_DONE_ADDED;
                                break;
                        case KEY_STATE_REQUEST:
-                               if (val != conn->session_param[idx].val) {
+                               if (val != conn->session_params[idx].val) {
                                        rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
                                        rsp->status_detail = ISCSI_STATUS_INIT_ERR;
                                        conn->state = STATE_EXIT;
                                        log_warning("%s %u %u\n", key,
-                                               val, conn->session_param[idx].val);
+                                               val, conn->session_params[idx].val);
                                        goto out;
                                }
-                               conn->session_param[idx].key_state = KEY_STATE_DONE;
+                               conn->session_params[idx].key_state = KEY_STATE_DONE;
                                break;
                        case KEY_STATE_DONE_ADDED:
                        case KEY_STATE_DONE:
@@ -395,9 +389,9 @@ out:
        return;
 }
 
-static int text_check_param(struct connection *conn)
+static int text_check_params(struct connection *conn)
 {
-       struct iscsi_param *p = conn->session_param;
+       struct iscsi_param *p = conn->session_params;
        char buf[32];
        int i, cnt;
 
@@ -426,7 +420,7 @@ static int text_check_param(struct connection *conn)
                                if (iscsi_is_key_internal(i))
                                        continue;
                                memset(buf, 0, sizeof(buf));
-                               param_val_to_str(session_keys, i, p[i].val, buf);
+                               params_val_to_str(session_keys, i, p[i].val, buf, sizeof(buf));
                                text_key_add(conn, session_keys[i].name, buf);
                                if (i == key_max_recv_data_length) {
                                        p[i].key_state = KEY_STATE_DONE;
@@ -445,6 +439,25 @@ static int text_check_param(struct connection *conn)
        return cnt;
 }
 
+static int init_conn_session_params(struct connection *conn)
+{
+       int res = 0, i;
+       struct target *target;
+
+       target = target_find_by_id(conn->tid);
+       if (target == NULL) {
+               log_error("target %d not found", conn->tid);
+               res = -EINVAL;
+               goto out;
+       }
+
+       for (i = 0; i < session_key_last; i++)
+               conn->session_params[i].val = target->session_params[i];
+
+out:
+       return res;
+}
+
 static void login_start(struct connection *conn)
 {
        struct iscsi_login_req_hdr *req = (struct iscsi_login_req_hdr *)&conn->req.bhs;
@@ -479,9 +492,9 @@ static void login_start(struct connection *conn)
        conn->session_type = SESSION_NORMAL;
 
        if (session_type) {
-               if (!strcmp(session_type, "Discovery"))
+               if (!strcmp(session_type, "Discovery")) {
                        conn->session_type = SESSION_DISCOVERY;
-               else if (strcmp(session_type, "Normal")) {
+               else if (strcmp(session_type, "Normal")) {
                        rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
                        rsp->status_detail = ISCSI_STATUS_INV_SESSION_TYPE;
                        conn->state = STATE_EXIT;
@@ -509,7 +522,7 @@ static void login_start(struct connection *conn)
                }
 
                if (!target->tgt_enabled) {
-                       log_debug(1, "Connect from %s to disabled target %s",
+                       log_info("Connect from %s to disabled target %s refused",
                                name, target_name);
                        rsp->status_class = ISCSI_STATUS_TARGET_ERR;
                        conn->state = STATE_CLOSE;
@@ -527,15 +540,9 @@ static void login_start(struct connection *conn)
                        return;
                }
 
-               err = kernel_param_get(conn->tid, conn->sid.id64, key_session,
-                       conn->session_param);
-               if (err == -ENOENT) {
-                       err = kernel_param_get(conn->tid, 0, key_session,
-                               conn->session_param);
-               }
-
+               err = init_conn_session_params(conn);
                if (err != 0) {
-                       log_error("Can't get session param for session 0x%" PRIu64 
+                       log_error("Can't get session params for session 0x%" PRIu64 
                                " (err %d): %s\n", conn->sid.id64, err,
                                strerror(-err));
                        rsp->status_class = ISCSI_STATUS_TARGET_ERR;
@@ -694,20 +701,20 @@ static void cmnd_exec_login(struct connection *conn)
                        conn->state = STATE_LOGIN;
 
                        login_start(conn);
-                       if (!account_empty(conn->tid, AUTH_DIR_INCOMING))
+                       if (!accounts_empty(conn->tid, ISCSI_USER_DIR_INCOMING))
                                goto auth_err;
                        if (rsp->status_class)
                                return;
                        text_scan_login(conn);
                        if (rsp->status_class)
                                return;
-                       stay = text_check_param(conn);
+                       stay = text_check_params(conn);
                        break;
                case STATE_LOGIN:
                        text_scan_login(conn);
                        if (rsp->status_class)
                                return;
-                       stay = text_check_param(conn);
+                       stay = text_check_params(conn);
                        break;
                default:
                        goto init_err;
@@ -737,7 +744,7 @@ static void cmnd_exec_login(struct connection *conn)
                        switch (conn->state) {
                        case STATE_SECURITY:
                        case STATE_SECURITY_DONE:
-                               if ((nsg_disagree = text_check_param(conn))) {
+                               if ((nsg_disagree = text_check_params(conn))) {
                                        conn->state = STATE_LOGIN;
                                        nsg = ISCSI_FLG_NSG_LOGIN;
                                        break;
@@ -755,7 +762,7 @@ static void cmnd_exec_login(struct connection *conn)
                        }
                        if (!stay && !nsg_disagree) {
                                int err;
-                               text_check_param(conn);
+                               text_check_params(conn);
                                err = login_finish(conn);
                                if (err != 0) {
                                        log_debug(1, "login_finish() failed: %d", err);
index 0105a57..33a536d 100644 (file)
@@ -68,7 +68,9 @@ struct connection {
        struct session *sess;
 
        u32 tid;
-       struct iscsi_param session_param[session_key_last];
+
+       /* Put here, because negotiations is done before session created */
+       struct iscsi_param session_params[session_key_last];
 
        char *initiator;
        char *user;
@@ -128,10 +130,6 @@ struct connection {
 #define AUTH_STATE_START       0
 #define AUTH_STATE_CHALLENGE   1
 
-/* don't touch these */
-#define AUTH_DIR_INCOMING       0
-#define AUTH_DIR_OUTGOING       1
-
 #define SESSION_NORMAL         0
 #define SESSION_DISCOVERY      1
 #define AUTH_UNKNOWN           -1
@@ -148,6 +146,21 @@ struct connection {
  */
 #define INCOMING_BUFSIZE       8192
 
+struct iscsi_user {
+       struct __qelem ulist;
+
+       struct target *target;
+
+#define ISCSI_USER_DIR_INCOMING                0
+#define ISCSI_USER_DIR_OUTGOING                1
+       int direction;
+
+       const char *name;
+       const char *password;
+
+       char sysfs_name[64];
+};
+
 struct target {
        struct __qelem tlist;
 
@@ -155,10 +168,16 @@ struct target {
 
        unsigned int tgt_enabled:1;
 
+       unsigned int target_params[target_key_last];
+       unsigned int session_params[session_key_last];
+
        u32 tid;
        char name[ISCSI_NAME_LEN];
        char *alias;
 
+       struct __qelem target_in_accounts;
+       struct __qelem target_out_accounts;
+
        struct __qelem isns_head;
 };
 
@@ -182,12 +201,12 @@ extern uint16_t server_port;
 extern void isns_set_fd(int isns, int scn_listen, int scn);
 
 /* iscsid.c */
-extern int iscsi_debug;
+extern int iscsi_enabled;
 
 extern int cmnd_execute(struct connection *conn);
 extern void cmnd_finish(struct connection *conn);
 extern char *text_key_find(struct connection *conn, char *searchKey);
-extern void text_key_add(struct connection *conn, char *key, char *value);
+extern void text_key_add(struct connection *conn, char *key, const char *value);
 
 /* log.c */
 extern int log_daemon;
@@ -219,8 +238,10 @@ extern struct connection *conn_find(struct session *session, u16 cid);
 
 /* target.c */
 extern struct __qelem targets_list;
-extern int target_add(u32 *, char *);
-extern int target_del(u32);
+extern int target_create(const char *name, struct target **out_target);
+extern void target_free(struct target *target);
+extern int target_add(struct target *target, u32 *tid, u32 cookie);
+extern int target_del(u32 tid, u32 cookie);
 extern u32 target_find_id_by_name(const char *name);
 extern struct target *target_find_by_name(const char *name);
 struct target *target_find_by_id(u32);
@@ -232,13 +253,19 @@ extern int iscsi_adm_request_handle(int accept_fd);
 
 /* ctldev.c */
 extern int kernel_open(int *max_data_seg_len);
-extern int kernel_param_get(u32 tid, u64 sid, int type, struct iscsi_param *param);
-extern int kernel_param_set(u32 tid, u64 sid, int type, u32 partial,
-       struct iscsi_param *param);
-extern int kernel_target_create(u32 *tid, char *name);
-extern int kernel_target_destroy(u32 tid);
-extern int kernel_session_create(u32 tid, u64 sid, u32 exp_cmd_sn,
-       char *name, char *user);
+extern int kernel_params_get(u32 tid, u64 sid, int type, struct iscsi_param *params);
+extern int kernel_params_set(u32 tid, u64 sid, int type, u32 partial,
+       const struct iscsi_param *params);
+extern int kernel_target_create(struct target *target, u32 *tid, u32 cookie);
+extern int kernel_target_destroy(u32 tid, u32 cookie);
+#ifndef CONFIG_SCST_PROC
+extern int kernel_user_add(struct iscsi_user *user, u32 cookie);
+extern int kernel_user_del(struct iscsi_user *user, u32 cookie);
+extern int kernel_attr_add(struct target *target, const char *name,
+       u32 mode, u32 cookie);
+extern int kernel_attr_del(struct target *target, const char *name, u32 cookie);
+#endif
+extern int kernel_session_create(struct connection *conn);
 extern int kernel_session_destroy(u32 tid, u64 sid);
 extern int kernel_conn_create(u32 tid, u64 sid, u32 cid, u32 stat_sn, u32 exp_stat_sn,
        int fd);
@@ -248,26 +275,38 @@ extern int kernel_conn_destroy(u32 tid, u64 sid, u32 cid);
 extern void handle_iscsi_events(int fd);
 extern int nl_open(void);
 
-/* param.c */
-extern int param_index_by_name(char *name, struct iscsi_key *keys);
-
 /* config.c */
-extern int config_isns_load(char *params, char **isns, int *isns_ac);
-extern int config_load(char *params);
+extern char *config_sep_string(char **pp);
+extern int config_parse_main(const char *data, u32 cookie);
+extern int config_load(const char *config_name);
 extern int config_target_create(u32 *tid, char *name);
 extern int config_target_destroy(u32 tid);
-int config_account_add(u32 tid, int dir, char *name, char *pass);
-extern int config_account_query(u32 tid, int dir, char *name, char *pass);
+extern int config_account_add(u32 tid, int dir, char *name, char *pass,
+       char *sysfs_name, u32 cookie);
+extern int __config_account_add(struct target *target, int dir, char *name,
+       char *pass, char *sysfs_name, int send_to_kern, u32 cookie);
+extern int config_account_query(u32 tid, int dir, const char *name, char *pass);
 extern int config_account_list(u32 tid, int dir, u32 *cnt, u32 *overflow,
        char *buf, size_t buf_sz);
-extern int config_account_del(u32 tid, int dir, char *name);
-extern int config_param_set(u32 tid, u64 sid, int type, u32 partial,
-       struct iscsi_param *param);
+extern int config_account_del(u32 tid, int dir, char *name, u32 cookie);
+extern int config_params_get(u32 tid, u64 sid, int type, struct iscsi_param *params);
+extern int config_params_set(u32 tid, u64 sid, int type, u32 partial,
+       struct iscsi_param *params);
 extern int config_initiator_access(u32 tid, int fd);
+extern int accounts_empty(u32 tid, int dir);
+extern struct iscsi_user *account_get_first(u32 tid, int dir);
+extern struct iscsi_user *account_lookup_by_sysfs_name(struct target *target,
+       int dir, const char *sysfs_name);
+extern int account_replace(struct target *target, int direction,
+       const char *sysfs_name, char *value);
+extern void accounts_free(struct __qelem *accounts_list);
 
 /* isns.c */
-extern int isns_init(char *addr, int isns_ac);
-extern int isns_handle(int is_timeout, int *timeout);
+extern char *isns_server;
+extern int isns_access_control;
+extern int isns_timeout;
+extern int isns_init(void);
+extern int isns_handle(int is_timeout);
 extern int isns_scn_handle(int accept);
 extern int isns_scn_access(u32 tid, int fd, char *name);
 extern int isns_target_register(char *name);
index 482e178..86c7841 100644 (file)
@@ -54,9 +54,13 @@ struct isns_initiator {
        struct __qelem ilist;
 };
 
+char *isns_server;
+int isns_access_control;
+int isns_timeout = -1;
+
 static LIST_HEAD(qry_list);
 static uint16_t scn_listen_port;
-static int use_isns, use_isns_ac, isns_fd, scn_listen_fd, scn_fd;
+static int isns_fd, scn_listen_fd, scn_fd;
 static struct isns_io isns_rx, scn_rx;
 static char *rxbuf;
 static uint16_t transaction;
@@ -70,11 +74,11 @@ int isns_scn_access(uint32_t tid, int fd, char *name)
        struct isns_initiator *ini;
        struct target *target = target_find_by_id(tid);
 
-       if (!use_isns || !use_isns_ac)
+       if ((isns_server == NULL) || !isns_access_control)
                return 0;
 
        if (!target)
-               return -EPERM;
+               return -ENOENT;
 
        list_for_each_entry(ini, &target->isns_head, ilist) {
                if (!strcmp(ini->name, name))
@@ -130,19 +134,25 @@ static int isns_connect(void)
 {
        int fd, err;
 
+       log_debug(1, "Going to connect to iSNS server %s", isns_server);
+
        fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
        if (fd < 0) {
                log_error("unable to create (%s) %d!", strerror(errno),
                          ss.ss_family);
-               return -1;
+               return -errno;
        }
 
+       /*
+        * ToDo: must be made non-blocking, otherwise for an unreacheable
+        * server it blocks all other events processing until timeout (30 secs).
+        */
        err = connect(fd, (struct sockaddr *) &ss, sizeof(ss));
        if (err < 0) {
                log_error("unable to connect (%s) %d!", strerror(errno),
                          ss.ss_family);
                close(fd);
-               return -1;
+               return -errno;
        }
 
        log_error("%s %d: new connection %d", __func__, __LINE__, fd);
@@ -151,7 +161,7 @@ static int isns_connect(void)
                err = isns_get_ip(fd);
                if (err) {
                        close(fd);
-                       return -1;
+&n