+config options 'tgt'
+# iothreads limits number of worker threads per rdwr target, default is 16
+# which seems to be too much for an avarage router
+ option iothreads '2'
+# option nop_count '3'
+# option nop_interval '1'
+# list portal '[::1]'
+# list portal '127.0.0.1:3261'
+# list portal '0.0.0.0:3262'
+# list portal '[::]:3263'
+
config target 1
option name 'iqn.2012-06.org.openwrt:target1'
- option allow ALL
+# list allow_name 'iqn.1994-05.org.example:fedcba987654'
+# list allow_address '172.19.218.128/27'
#config target 2
# option name 'iqn.2012-06.org.openwrt:t2'
-# option allow 192.168.1.0/24
-
# all options are set to default, except for the device
-# for all type and bstype values see tgtd(8)
# lun "name" is constructed as TGTID_LUN
#config lun 1_1
-# option readonly 0
-# option device /dev/sda
-# option type disk
-# option bstype rdwr
-# option sync 0
-# option direct 0
-
+# option 'device' '/dev/sda'
+# type of scsi device. available options: disk, cd, pt (sg passthrough)
+# option 'type' 'disk'
+# backing store access method: rdwr (read-write), aio (async IO), sg (for pt type only, device must be /dev/sgN)
+# option 'bstype' 'aio'
+# set sync and/or direct flags when opening device, affect only rdwr
+# option 'sync' '0'
+# option 'direct' '0'
+# block size for lun, default is 512
+# option 'blocksize' 4096
+# override SCSI mode page, see tgtadm man page for details
+# option 'mode_page' 'string'
+# vendor, product, revision, SCSI ID and SCSI Serial number
+# option 'vendor_id' 'string'
+# option 'product_id' 'string'
+# option 'product_rev' 'string'
+# option 'scsi_id' 'string'
+# option 'scsi_sn' 'string'
+# refuse write attempts. applies only to disk type
+# option 'readonly' '0'
+# Disk devices default to non-removable, cd - to removable
+# option 'removable' '0'
+# 0 = Clasic sense format, 1 = Support descriptor format.
+# option 'sense_format' '0'
#config lun 2_1
-# option readonly 0
# option device /mnt/iscsi.img
#config lun 2_2
# option device /dev/sdc
#config account
-# option target 1
+# list target 1
+# list target 2
# option user "username1"
# option password "pass1"
USE_PROCD=1
tgtadm="/usr/sbin/tgtadm --lld iscsi"
+logger="logger -p daemon.err -s -t $NAME"
+
+validate_lun_section() {
+ uci_validate_section tgt lun $1 \
+ 'device:file' \
+ 'type:or("disk", "cd", "pt"):disk' \
+ 'bstype:or("rdwr", "aio", "sg"):rdwr' \
+ 'sync:bool:0' \
+ 'direct:bool:0' \
+ 'blocksize:uinteger' \
+ 'mode_page:string' \
+ 'product_id:string' \
+ 'product_rev:string' \
+ 'readonly:bool:0' \
+ 'removable:bool' \
+ 'scsi_id:string' \
+ 'scsi_sn:string' \
+ 'sense_format:range(0, 1)' \
+ 'vendor_id:string'
+}
handle_lun() {
local tgt_lun=$1
local tgtid=$2
- local ro device type bstype sync direct
+ local readonly device type bstype sync direct
local my_tgtid=${tgt_lun%_*}
local lun=${tgt_lun#*_}
+
[ $my_tgtid -eq $tgtid ] || return 0
- config_get device $1 device ""
- [ "$device" ] || return 1
+ validate_lun_section $tgt_lun || {
+ $logger "Validation failed for LUN $tgt_lun"
+ return 1
+ }
+ [ "$device" ] || {
+ $logger "Device is required for target $tgt_lun"
+ return 1
+ }
- config_get type $1 type disk
- config_get bstype $1 bstype rdwr
- config_get_bool readonly $1 readonly 0
- config_get_bool sync $1 sync 0
- config_get_bool direct $1 direct 0
if [ $sync -ne 0 -o $direct -ne 0 ]; then
local bsoflags
[ $sync -ne 0 ] && bsoflags="sync"
bsoflags="--bsoflags $bsoflags"
fi
+ blocksize=${blocksize+--blocksize=$blocksize}
+ local params='' i
+ for i in mode_page product_id product_rev readonly removable scsi_id scsi_sn sense_format vendor_id; do
+ eval params=\${$i+$i=\$$i,}\$params
+ done
+
local _tgtadm="$tgtadm --mode logicalunit --tid $tgtid --lun $lun"
- $_tgtadm --op new --backing-store $device --device-type $type --bstype $bstype --bstype $bstype $bsoflags || return 1
- $_tgtadm --op update --param readonly=$readonly || return 1
+ $_tgtadm --op new --backing-store $device --device-type $type --bstype $bstype --bstype $bstype $bsoflags $blocksize || {
+ $logger "Failed to create lun $tgt_lun"
+ return 1
+ }
+ $_tgtadm --op update --param $params || {
+ $logger "Failed to update lun $tgt_lun"
+ return 1
+ }
+}
+
+validate_account_section () {
+ uci_validate_section tgt account $1 \
+ 'target:list(uinteger)' \
+ 'user:string' \
+ 'password:string' \
+ 'outgoing:bool:0'
}
handle_account() {
- local tgtid=$2
local _tgtadm="$tgtadm --mode account"
- local target user password outgoing
+ local user password target outgoing
+
+ validate_account_section $1 || {
+ $logger "Validation failed for account ${user:-$1}"
+ return 1
+ }
+ [ "$user" ] || {
+ $logger "User is required for account $1. Run 'uci show tgt.$1' and check options"
+ return 1
+ }
+ [ "$target" ] || {
+ $logger "Target is required for account $user"
+ return 1
+ }
+ [ "$password" ] || {
+ $logger "Password is required for account $user"
+ return 1
+ }
+ $_tgtadm --op new --user "$user" --password "$password" || {
+ $logger "Failed to create user $username"
+ return 1
+ }
+}
- config_get target $1 target ""
- [ "$target" ] || return 1
- [ $target -eq $tgtid ] || return 0
+bind_account_to_target() {
+ local _tgtadm="$tgtadm --mode account"
+ local tgtid=$2 user password outgoing
- config_get user $1 user ""
- [ "$user" ] || return 1
+ validate_account_section $1 || {
+ $logger "Validation failed for account ${user:-$1}"
+ return 1
+ }
- config_get password $1 password ""
- config_get_bool outgoing $1 outgoing 0
[ "$outgoing" -ne 0 ] && outgoing=--outgoing || outgoing=""
+ local t
+ for t in $target; do
+ [ "$t" -eq "$tgtid" ] && {
+ $_tgtadm --op bind --tid $tgtid --user "$user" $outgoing || {
+ $logger "Failed to bind user $username to target $tgtid"
+ return 1
+ }
+ }
+ done
+ return 0
+}
- $_tgtadm --op new --user "$user" --password "$password" || return 1
- $_tgtadm --op bind --tid $tgtid --user "$user" $outgoing || return 1
+validate_target_section() {
+ uci_validate_section tgt target $1 \
+ 'name:string:iqn.2012-06.org.openwrt' \
+ 'allow_address:list(string):ALL' \
+ 'allow_name:list(string)'
}
handle_target() {
local tgtid=$1
- local tgtname allow
local _tgtadm="$tgtadm --mode target"
+ local name allow
[ $tgtid -ge 0 ] || return 1
-
- config_get tgtname $1 name iqn.2012-06.org.openwrt
- config_get allow $1 allow ALL
-
- $_tgtadm --op new --tid $tgtid --targetname $tgtname || return 1
- $_tgtadm --op bind --tid $tgtid -I $allow || return 1
-
+ validate_target_section $tgtid || {
+ $logger "Validation failed for target $tgtid"
+ return 1
+ }
+ $_tgtadm --op new --tid $tgtid --targetname $name || {
+ $logger "Failed to create target $tgtid"
+ return 1
+ }
+ local i
+ for i in $allow_address; do
+ $_tgtadm --op bind --tid $tgtid --initiator-address $i || {
+ $logger "Failed to set allow $i to connect to target $tgtid"
+ return 1
+ }
+ done
+ for i in $allow_name; do
+ $_tgtadm --op bind --tid $tgtid --initiator-name $i || {
+ $logger "Failed to set allow $i to connect to target $tgtid"
+ return 1
+ }
+ done
config_foreach handle_lun lun $tgtid || return 1
- config_foreach handle_account account $tgtid || return 1
+ config_foreach bind_account_to_target account $tgtid || return 1
}
configure() {
config_load $NAME
- $tgtadm --mode sys --op update --name State -v offline || return 1
+ $tgtadm --mode sys --op update --name State -v offline || {
+ $logger "Failed to set system state to Offline"
+ return 1
+ }
+ config_foreach handle_account account || return 1
config_foreach handle_target target || return 1
- $tgtadm --mode sys --op update --name State -v ready || return 1
+ $tgtadm --mode sys --op update --name State -v ready || {
+ $logger "Failed to set system state to Ready"
+ return 1
+ }
return 0
}
+validate_tgt_section() {
+ uci_validate_section tgt options $1 \
+ 'iothreads:uinteger' \
+ 'portal:list(string)' \
+ 'nop_interval:uinteger' \
+ 'nop_count:uinteger'
+}
+
start_service() {
+ local iothreads portal nop_interval nop_count
+ validate_tgt_section tgt || {
+ $logger "Validation failed for tgt options"
+ return 1
+ }
procd_open_instance
procd_set_param command $PROG -f
+ [ "$iothreads" ] && procd_append_param command -t $iothreads
+ [ "${portal}${nop_interval}${nop_count}" ] && {
+ local iscsi="" i
+ for i in nop_interval nop_count; do
+ eval iscsi=\${$i+$i=\$$i,}\$iscsi
+ done
+ for i in $portal; do
+ iscsi="portal=$i,$iscsi"
+ done
+ procd_append_param command --iscsi $iscsi
+ }
procd_set_param respawn
procd_close_instance
- logger -t $NAME -s "Configuration will be loaded in seconds"
+ logger -p daemon.info -t $NAME -s "Configuration will be loaded in seconds"
( sleep 5; configure || { stop_service; exit 1; } ) &
}
stop_service() {
- $tgtadm --mode sys --op update --name State -v offline
- $tgtadm --mode target --op show \
- | awk '$1 == "Target" {sub(/:/,"",$2); print $2}' \
- | xargs -n1 $tgtadm --mode target --op delete --force --tid
+ $tgtadm --mode sys --op update --name State -v offline || {
+ $logger "Failed to set system state to Offline"
+ return 1
+ }
+ $tgtadm --mode target --op show \
+ | awk '$1 == "Target" {sub(/:/,"",$2); print $2}' \
+ | xargs -r -n1 $tgtadm --mode target --op delete --force --tid
$tgtadm --mode sys --op delete
}