rc script for iSCSI initiator

Miroslav Lachman 000.fbsd at quip.cz
Thu Jan 28 23:24:19 UTC 2010


Hi,

iSCSI initiator is in the base for a long time, but there is not 
rc.d/iscsi script to start iSCSI sessions. I found one old in mailing 
list and take it as base for my work.
I attached two versions.

*rc.d_iscsi.all.txt* manage all defined targets at start / stop command

*rc.d_iscsi.individual.txt* can selectively start / stop individual 
sessions (targets defined in /etc/iscsi.conf and in rc.conf 
iscsi_targets="")

It is not intended to direct commit, it is just work in progress and 
needs some discussion and improvements from rc folks.

Scripts are too verbose for better debugging.

Both of them can be called as:

/etc/rc.d/iscsi start|stop

The "individual" version can be called:

/etc/rc.d/iscsi start|stop [target_name]

For example:

# /etc/rc.d/iscsi start styx_storage_1
debug.iscsi_initiator: 2 -> 0
Starting iSCSI session for styx_storage_1
Waiting for /dev/ufs/styx0p1: . OK
/dev/ufs/styx0p1: FILE SYSTEM CLEAN; SKIPPING CHECKS
/dev/ufs/styx0p1: clean, 856565848 free (896 frags, 107070619 blocks, 
0.0% fragmentation)
/dev/ufs/styx0p1 on /vol1 (ufs, local, soft-updates, fsid 2d3d5e4b8b67f290)


# /etc/rc.d/iscsi stop styx_storage_1
Unmounting iSCSI volumes for styx_storage_1
/dev/ufs/styx0p1: unmount from /vol1
Stopping iSCSI session for: styx_storage_1
.....All iSCSI sessions were stopped


My main problem with this script is how to get a PID of started 
iscontrol and as I didn't found a "right way" to do it, the script 
contains some ugly hack with writing temporary file. This must be fixed 
by somebody more skilled than me.

I am open to any comments, sugesstions, warnings etc.

Miroslav Lachman
-------------- next part --------------
#!/bin/sh

# PROVIDE: iscsi
# REQUIRE: NETWORKING
# BEFORE:  DAEMON
# KEYWORD: nojail shutdown

#
# This script supports starting and stopping individual sessions
# from the iscsi_targets list:
# rc.d/iscsi start my_first_target
# rc.d/iscsi stop another_target
#
# Targets must be defined in /etc/iscsi.conf
# Each target has its own fstab file named as $iscsi_fstab.target_name
#
# Add the following lines to /etc/rc.conf to enable iscsi:
#
# iscsi_enable="YES"
# iscsi_targets=""	# list of targets defined in /etc/iscsi.conf
# iscsi_fstab="/etc/fstab.iscsi"	# prefix for per target fstabs
# iscsi_debug=""	# 0,1,2
# iscsi_exports=""
# iscsi_zfs=""
#

. /etc/rc.subr

name=iscsi
rcvar=`set_rcvar`

command=/sbin/iscontrol

iscsi_enable=${iscsi_enable:-"NO"}
iscsi_fstab=${iscsi_fstab:-"/etc/fstab.iscsi"}
iscsi_exports=${iscsi_exports:-"/etc/exports.iscsi"}
iscsi_debug=${iscsi_debug:-0}
start_cmd="iscsi_start"
faststop_cmp="iscsi_stop"
stop_cmd="iscsi_stop"
stop_postcmd="iscsi_poststop"
required_modules="iscsi_initiator:iscsi"


iscsi_wait()
{
	dev=$1
	trap "echo 'Wait loop cancelled'; exit 1" 2
	count=0
	while true; do
		if [ -c $dev ]; then
			break;
		fi
		if [ $count -eq 0 ]; then
			echo -n "Waiting for ${dev}: "
		fi
		count=$((${count} + 1))
		if [ $count -eq 6 ]; then
			echo ' FAILED'
			return 0
			break
		fi
		echo -n '.'
		sleep 5;
	done
	echo ' OK'
	return 1
}

iscsi_start()
{

	sysctl debug.iscsi_initiator=$iscsi_debug
	#
	# start iscontrol for each target
	if [ -n "${iscsi_targets}" ]; then
		_tmp_dir=`mktemp -d /tmp/iscsi.XXXXXXXX` || \
			err 3 "$name: Can't create temp dir, exiting..."

		for target in ${iscsi_targets}; do
			_pid_file="/var/run/iscontrol_${target}.pid"

			if [ -f "${_pid_file}" ]; then
				pgrep -F "${_pid_file}" iscontrol > /dev/null
				_return=$?
				if [ $_return -eq 0 ]; then
					echo "Session for ${target} is already running"
					continue
				fi
				echo "Removing stale pidfile ${_pid_file}"
				rm "${_pid_file}"
			fi

			echo "Starting iSCSI session for ${target}"
			_tmp_iscsi=${_tmp_dir}/iscontrol.${target}

			${command} ${rc_flags} -n ${target} > ${_tmp_iscsi} 2>&1

			# I do not know the better way to get iscontrol PID
			head -1 "${_tmp_iscsi}" | sed -E 's/.*\[([0-9]*)\].*/\1/' > "${_pid_file}"
#			rm -f "$_tmp_iscsi"
			## file specified as _tmp_iscsi remains open until iscontrol is closed
			## lsof +aL1 /tmp   # lists unlinked files, still opened
		done
#		rmdir "${_tmp_dir}"
	else
		# no targets, no devices, no mounts, just exit
		warn 'You must specify iscsi_targets list'
		return 1
	fi

	# We have separate fstab file for each target (/etc/fstab.iscsi.target_name)
	for target in ${iscsi_targets}; do
		target_fstab="${iscsi_fstab}.${target}"
		if [ -f "${target_fstab}" ]; then
			while read spec file type opt t1 t2
			do
				case ${spec} in
				\#*|'')
				;;
				*)
					if iscsi_wait ${spec}; then
						break;
					fi

					# Check if not already mounted and run fsck
					mount | grep  "^${spec} "
					if [ $? -ne 0 ]; then
						#echo "type=${type} spec=${spec} file=${file} opt=${opt}"
						fsck -p -t ${type} ${spec}
					else
						debug "${spec} is already mounted, skipping fsck"
					fi
				;;
				esac
			done < "${target_fstab}"

			# Mount all filesystems from given fstab file
			mount -a -v -F "${target_fstab}"
		fi
	done

	if [ -f "${iscsi_exports}" ]; then
		# ??? same content will be appended on each start!
		# it should be checked with diff / uniq
		cat ${iscsi_exports} >> /etc/exports
		#/etc/rc.d/mountd reload
		kill -1 `cat /var/run/mountd.pid`
	fi

	if checkyesno zfs_enable; then
		if checkyesno iscsi_zfs; then
			sleep 4
			zpool import -d /dev/label -a
			# presumably we can ignore zfs mount -a share -a and swapon
			# some zfs dataset may be exported "legacy", so hup mountd
			kill -1 `cat /var/run/mountd.pid`
		fi
	fi
}

iscsi_stop()
{
	for target in ${iscsi_targets}; do
                target_fstab="${iscsi_fstab}.${target}"
		if [ -f "${target_fstab}" ]; then
			echo "Unmounting iSCSI volumes for ${target}"
			umount -a -fv -F "${target_fstab}"
		fi
	done

	echo -n 'Stopping iSCSI session for:'
	_poststop=0
	# Stop iscontrol for each target
	if [ -n "${iscsi_targets}" ]; then
		if [ `sysctl -n net.iscsi.sessions` -gt 0 ]; then
			# We will run iscsi_poststop loop
			_poststop=1
		fi

		for target in ${iscsi_targets}; do
			echo -n " ${target}"
			_iscsi_pid_file="/var/run/iscontrol_${target}.pid"

			if [ -f "${_iscsi_pid_file}" ]; then
				_pid=$(head -1 "${_iscsi_pid_file}")
				# Safety check if PID from file is PID of iscontrol
				_grep_pid="$(pgrep -F "${_iscsi_pid_file}" iscontrol)"

				if [ "${_pid}" = "${_grep_pid}" ]; then
					kill -HUP ${_pid} && rm "${_iscsi_pid_file}"
					_pids="${_pids} $_pid"
				fi
			else
				echo -e "\n   Cannot stop session ${target}. No PID in /var/run"
			fi
		done
	fi
	echo ''
}

iscsi_poststop()
{
	_dying=0
	while [ $_poststop -gt 0 -a -n "${_pids}" ];
	do
		if [ -n "$(ps -o pid= -o command= -p ${_pids})" ]; then
			sleep 2
			echo -n '.'
			_dying=$(($_dying + 1))

			if [ $_dying -gt 10 ]; then
				echo 'Some session does not want stop, giving up'
				return 1
				break
			fi
		else
			echo 'All iSCSI sessions were stopped'
			return 0
			break
		fi
	done

}

load_rc_config $name
cmd="$1"
if [ $# -gt 0 ]; then
	shift
fi
if [ -n "$*" ]; then
	iscsi_targets="$*"
fi
run_rc_command "${cmd}"
-------------- next part --------------
#!/bin/sh

# PROVIDE: iscsi
# REQUIRE: NETWORKING
# BEFORE:  DAEMON
# KEYWORD: nojail shutdown

#
# Add the following lines to /etc/rc.conf to enable iscsi:
#
# iscsi_enable="YES"
# iscsi_targets=""	# list of targets defined in /etc/iscsi.conf
# iscsi_fstab="/etc/fstab.iscsi"
# iscsi_debug=""	# 0,1,2
# iscsi_exports=""
# iscsi_zfs=""
#

. /etc/rc.subr

name=iscsi
rcvar=`set_rcvar`

command=/sbin/iscontrol

iscsi_enable=${iscsi_enable:-"NO"}
iscsi_fstab=${iscsi_fstab:-"/etc/fstab.iscsi"}
iscsi_exports=${iscsi_exports:-"/etc/exports.iscsi"}
iscsi_debug=${iscsi_debug:-0}
start_cmd="iscsi_start"
faststop_cmp="iscsi_stop"
stop_cmd="iscsi_stop"
stop_postcmd="iscsi_poststop"
required_modules="iscsi_initiator:iscsi"


iscsi_wait()
{
	dev=$1
	trap "echo 'Wait loop cancelled'; exit 1" 2
	count=0
	while true; do
		if [ -c $dev ]; then
			break;
		fi
		if [ $count -eq 0 ]; then
			echo -n "Waiting for ${dev}: "
		fi
		count=$((${count} + 1))
		if [ $count -eq 6 ]; then
			echo ' FAILED'
			return 0
			break
		fi
		echo -n '.'
		sleep 5;
	done
	echo ' OK'
	return 1
}

iscsi_start()
{

	sysctl debug.iscsi_initiator=$iscsi_debug
	#
	# start iscontrol for each target
	if [ -n "${iscsi_targets}" ]; then
		_tmp_dir=`mktemp -d /tmp/iscsi.XXXXXXXX` || \
			err 3 "$name: Can't create temp dir, exiting..."

		for target in ${iscsi_targets}; do
			_pid_file="/var/run/iscontrol_${target}.pid"

			if [ -f "${_pid_file}" ]; then
				pgrep -F "${_pid_file}" iscontrol > /dev/null
				_return=$?
				if [ $_return -eq 0 ]; then
					echo "Session for ${target} is already running"
					continue
				fi
				echo "Removing stale pidfile ${_pid_file}"
				rm "${_pid_file}"
			fi

			echo "Starting iSCSI session for ${target}"
			_tmp_iscsi=${_tmp_dir}/iscontrol.${target}

			${command} ${rc_flags} -n ${target} > ${_tmp_iscsi} 2>&1

			# I do not know the better way to get iscontrol PID
			head -1 "${_tmp_iscsi}" | sed -E 's/.*\[([0-9]*)\].*/\1/' > "${_pid_file}"
#			rm -f "$_tmp_iscsi"
			## file specified as _tmp_iscsi remains open until iscontrol is closed
			## lsof +aL1 /tmp   # lists unlinked files, still opened
		done
#		rmdir "${_tmp_dir}"
	else
		# no targets, no devices, no mounts, just exit
		warn 'You must specify iscsi_targets list'
		return 1
	fi

	if [ -f "${iscsi_fstab}" ]; then
		while read spec file type opt t1 t2
		do
			case ${spec} in
			\#*|'')
			;;
			*)
				if iscsi_wait ${spec}; then
					break;
				fi

				# Check if not already mounted and run fsck
				mount | grep  "^${spec} "
				if [ $? -ne 0 ]; then
					#echo "type=${type} spec=${spec} file=${file} opt=${opt}"
					fsck -p -t ${type} ${spec}
				else
					debug "${spec} is already mounted, skipping fsck"
				fi
			;;
			esac
		done < ${iscsi_fstab}

		# Mount all filesystems from given fstab file
		mount -a -v -F "${iscsi_fstab}"
	fi

	if [ -f "${iscsi_exports}" ]; then
		# ??? same content will be appended on each start!
		# it should be checked with diff / uniq
		cat ${iscsi_exports} >> /etc/exports
		#/etc/rc.d/mountd reload
		kill -1 `cat /var/run/mountd.pid`
	fi

	if checkyesno zfs_enable; then
		if checkyesno iscsi_zfs; then
			sleep 4
			zpool import -d /dev/label -a
			# presumably we can ignore zfs mount -a share -a and swapon
			# some zfs dataset may be exported "legacy", so hup mountd
			kill -1 `cat /var/run/mountd.pid`
		fi
	fi
}

iscsi_stop()
{
	if [ -f "${iscsi_fstab}" ]; then
		echo 'Unmounting all iSCSI volumes:'
		umount -a -fv -F ${iscsi_fstab}
	fi

	echo -n 'Stopping iSCSI session for:'
	_poststop=0
	# Stop iscontrol for each target
	if [ -n "${iscsi_targets}" ]; then
		if [ `sysctl -n net.iscsi.sessions` -gt 0 ]; then
			# We will run iscsi_poststop loop
			_poststop=1
		fi

		for target in ${iscsi_targets}; do
			echo -n " ${target}"
			_iscsi_pid_file="/var/run/iscontrol_${target}.pid"

			if [ -f "${_iscsi_pid_file}" ]; then
				_pid=$(head -1 "${_iscsi_pid_file}")
				# Safety check if PID from file is PID of iscontrol
				_grep_pid="$(pgrep -F "${_iscsi_pid_file}" iscontrol)"

				if [ "${_pid}" = "${_grep_pid}" ]; then
					kill -HUP ${_pid} && rm "${_iscsi_pid_file}"
					_pids="${_pids} $_pid"
				fi
			else
				echo -e "\n   Cannot stop session ${target}. No PID in /var/run"
			fi
		done
	fi
	echo ''
}

iscsi_poststop()
{
	_dying=0
	while [ $_poststop -gt 0 -a -n "${_pids}" ];
	do
		if [ -n "$(ps -o pid= -o command= -p ${_pids})" ]; then
			sleep 2
			echo -n '.'
			_dying=$(($_dying + 1))

			if [ $_dying -gt 10 ]; then
				echo 'Some session does not want stop, giving up'
				return 1
				break
			fi
		else
			echo 'All iSCSI sessions were stopped'
			return 0
			break
		fi
	done

}

load_rc_config $name
run_rc_command "$1"


More information about the freebsd-rc mailing list