summaryrefslogtreecommitdiffstats
path: root/modules.d/95iscsi
diff options
context:
space:
mode:
Diffstat (limited to 'modules.d/95iscsi')
-rwxr-xr-xmodules.d/95iscsi/cleanup-iscsi.sh5
-rwxr-xr-xmodules.d/95iscsi/iscsiroot.sh322
-rwxr-xr-xmodules.d/95iscsi/module-setup.sh294
-rwxr-xr-xmodules.d/95iscsi/mount-lun.sh15
-rwxr-xr-xmodules.d/95iscsi/parse-iscsiroot.sh156
5 files changed, 792 insertions, 0 deletions
diff --git a/modules.d/95iscsi/cleanup-iscsi.sh b/modules.d/95iscsi/cleanup-iscsi.sh
new file mode 100755
index 0000000..8338503
--- /dev/null
+++ b/modules.d/95iscsi/cleanup-iscsi.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ -z "${DRACUT_SYSTEMD}" ] && { [ -e /sys/module/bnx2i ] || [ -e /sys/module/qedi ]; }; then
+ killproc iscsiuio
+fi
diff --git a/modules.d/95iscsi/iscsiroot.sh b/modules.d/95iscsi/iscsiroot.sh
new file mode 100755
index 0000000..b6af7f4
--- /dev/null
+++ b/modules.d/95iscsi/iscsiroot.sh
@@ -0,0 +1,322 @@
+#!/bin/sh
+#
+# This implementation is incomplete: Discovery mode is not implemented and
+# the argument handling doesn't follow currently agreed formats. This is mainly
+# because rfc4173 does not say anything about iscsi_initiator but open-iscsi's
+# iscsistart needs this.
+#
+
+type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh
+type parse_iscsi_root > /dev/null 2>&1 || . /lib/net-lib.sh
+type write_fs_tab > /dev/null 2>&1 || . /lib/fs-lib.sh
+
+PATH=/usr/sbin:/usr/bin:/sbin:/bin
+
+# Huh? Empty $1?
+[ -z "$1" ] && exit 1
+
+# Huh? Empty $2?
+[ -z "$2" ] && exit 1
+
+# Huh? Empty $3? This isn't really necessary, since NEWROOT isn't
+# used here. But let's be consistent
+[ -z "$3" ] && exit 1
+
+# root is in the form root=iscsi:[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
+netif="$1"
+iroot="$2"
+
+# If it's not iscsi we don't continue
+[ "${iroot%%:*}" = "iscsi" ] || exit 1
+
+iroot=${iroot#iscsi}
+iroot=${iroot#:}
+
+# XXX modprobe crc32c should go in the cmdline parser, but I haven't yet
+# figured out a way how to check whether this is built-in or not
+modprobe crc32c 2> /dev/null
+
+# start iscsiuio if needed
+if [ -z "${DRACUT_SYSTEMD}" ] \
+ && { [ -e /sys/module/bnx2i ] || [ -e /sys/module/qedi ]; } \
+ && ! [ -e /tmp/iscsiuio-started ]; then
+ iscsiuio
+ : > /tmp/iscsiuio-started
+fi
+
+handle_firmware() {
+ local ifaces retry _res
+
+ # Depending on the 'ql4xdisablesysfsboot' qla4xxx
+ # will be autostarting sessions without presenting
+ # them via the firmware interface.
+ # In these cases 'iscsiadm -m fw' will fail, but
+ # the iSCSI sessions will still be present.
+ if ! iscsiadm -m fw; then
+ warn "iscsiadm: Could not get list of targets from firmware."
+ else
+ ifaces=$(
+ set -- /sys/firmware/ibft/ethernet*
+ echo $#
+ )
+ read -r retry < /tmp/session-retry
+
+ if [ "$retry" -lt "$ifaces" ]; then
+ retry=$((retry + 1))
+ echo $retry > /tmp/session-retry
+ return 1
+ else
+ rm /tmp/session-retry
+ fi
+
+ # check to see if we have the new iscsiadm command,
+ # that supports the "no-wait" (-W) flag. If so, use it.
+ iscsiadm -m fw -l -W 2> /dev/null
+ _res=$?
+ if [ $_res -eq 7 ]; then
+ # ISCSI_ERR_INVALID (7) => "-W" not supported
+ info "iscsiadm does not support no-wait firmware logins"
+ iscsiadm -m fw -l
+ _res=$?
+ fi
+ if [ $_res -ne 0 ]; then
+ warn "iscsiadm: Log-in to iscsi target failed"
+ else
+ need_shutdown
+ fi
+ fi
+ [ -d /sys/class/iscsi_session ] || return 1
+ echo 'started' > "/tmp/iscsistarted-iscsi:"
+ echo 'started' > "/tmp/iscsistarted-firmware"
+
+ return 0
+}
+
+handle_netroot() {
+ local iscsi_initiator iscsi_target_name iscsi_target_ip iscsi_target_port
+ local iscsi_target_group iscsirw iscsi_lun
+ local iscsi_username iscsi_password
+ local iscsi_in_username iscsi_in_password
+ local iscsi_iface_name iscsi_netdev_name
+ local iscsi_param param
+ local p found
+ local login_retry_max_seen=
+
+ # override conf settings by command line options
+ arg=$(getarg rd.iscsi.initiator -d iscsi_initiator=)
+ [ -n "$arg" ] && iscsi_initiator=$arg
+ arg=$(getarg rd.iscsi.target.group -d iscsi_target_group=)
+ [ -n "$arg" ] && iscsi_target_group=$arg
+ arg=$(getarg rd.iscsi.username -d iscsi_username=)
+ [ -n "$arg" ] && iscsi_username=$arg
+ arg=$(getarg rd.iscsi.password -d iscsi_password)
+ [ -n "$arg" ] && iscsi_password=$arg
+ arg=$(getarg rd.iscsi.in.username -d iscsi_in_username=)
+ [ -n "$arg" ] && iscsi_in_username=$arg
+ arg=$(getarg rd.iscsi.in.password -d iscsi_in_password=)
+ [ -n "$arg" ] && iscsi_in_password=$arg
+ for p in $(getargs rd.iscsi.param -d iscsi_param); do
+ [ "${p%=*}" = node.session.initial_login_retry_max ] \
+ && login_retry_max_seen=yes
+ iscsi_param="$iscsi_param $p"
+ done
+
+ # this sets iscsi_target_name and possibly overwrites most
+ # parameters read from the command line above
+ parse_iscsi_root "$1" || return 1
+
+ # Bail out early, if there is no route to the destination
+ if is_ip "$iscsi_target_ip" && [ "$netif" != "timeout" ] && ! all_ifaces_setup && getargbool 1 rd.iscsi.testroute; then
+ ip route get "$iscsi_target_ip" > /dev/null 2>&1 || return 0
+ fi
+
+ #limit iscsistart login retries
+ if [ "$login_retry_max_seen" != yes ]; then
+ retries=$(getargnum 3 0 10000 rd.iscsi.login_retry_max)
+ if [ "$retries" -gt 0 ]; then
+ iscsi_param="${iscsi_param% } node.session.initial_login_retry_max=$retries"
+ fi
+ fi
+
+ # XXX is this needed?
+ getarg ro && iscsirw=ro
+ getarg rw && iscsirw=rw
+ fsopts=${fsopts:+$fsopts,}${iscsirw}
+
+ if [ -z "$iscsi_initiator" ] && [ -f /sys/firmware/ibft/initiator/initiator-name ] && ! [ -f /tmp/iscsi_set_initiator ]; then
+ iscsi_initiator=$(while read -r line || [ -n "$line" ]; do echo "$line"; done < /sys/firmware/ibft/initiator/initiator-name)
+ echo "InitiatorName=$iscsi_initiator" > /run/initiatorname.iscsi
+ rm -f /etc/iscsi/initiatorname.iscsi
+ mkdir -p /etc/iscsi
+ ln -fs /run/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi
+ : > /tmp/iscsi_set_initiator
+ if [ -n "$DRACUT_SYSTEMD" ]; then
+ systemctl try-restart iscsid
+ # FIXME: iscsid is not yet ready, when the service is :-/
+ sleep 1
+ fi
+ fi
+
+ if [ -z "$iscsi_initiator" ]; then
+ [ -f /run/initiatorname.iscsi ] && . /run/initiatorname.iscsi
+ [ -f /etc/initiatorname.iscsi ] && . /etc/initiatorname.iscsi
+ [ -f /etc/iscsi/initiatorname.iscsi ] && . /etc/iscsi/initiatorname.iscsi
+ iscsi_initiator=$InitiatorName
+ fi
+
+ if [ -z "$iscsi_initiator" ]; then
+ iscsi_initiator=$(iscsi-iname)
+ echo "InitiatorName=$iscsi_initiator" > /run/initiatorname.iscsi
+ rm -f /etc/iscsi/initiatorname.iscsi
+ mkdir -p /etc/iscsi
+ ln -fs /run/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi
+ : > /tmp/iscsi_set_initiator
+ if [ -n "$DRACUT_SYSTEMD" ]; then
+ systemctl try-restart iscsid
+ # FIXME: iscsid is not yet ready, when the service is :-/
+ sleep 1
+ fi
+ fi
+
+ if [ -z "$iscsi_target_port" ]; then
+ iscsi_target_port=3260
+ fi
+
+ if [ -z "$iscsi_target_group" ]; then
+ iscsi_target_group=1
+ fi
+
+ if [ -z "$iscsi_lun" ]; then
+ iscsi_lun=0
+ fi
+
+ echo "InitiatorName=$iscsi_initiator" > /run/initiatorname.iscsi
+ ln -fs /run/initiatorname.iscsi /dev/.initiatorname.iscsi
+ if ! [ -e /etc/iscsi/initiatorname.iscsi ]; then
+ mkdir -p /etc/iscsi
+ ln -fs /run/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi
+ if [ -n "$DRACUT_SYSTEMD" ]; then
+ systemctl try-restart iscsid
+ # FIXME: iscsid is not yet ready, when the service is :-/
+ sleep 1
+ fi
+ fi
+
+ if [ -z "$DRACUT_SYSTEMD" ]; then
+ iscsid
+ sleep 2
+ fi
+
+ # FIXME $iscsi_protocol??
+
+ if [ "$root" = "dhcp" ] || [ "$netroot" = "dhcp" ]; then
+ # if root is not specified try to mount the whole iSCSI LUN
+ printf 'SYMLINK=="disk/by-path/*-iscsi-*-%s", SYMLINK+="root"\n' "$iscsi_lun" >> /etc/udev/rules.d/99-iscsi-root.rules
+ udevadm control --reload
+ write_fs_tab /dev/root
+ wait_for_dev -n /dev/root
+
+ # install mount script
+ [ -z "$DRACUT_SYSTEMD" ] \
+ && echo "iscsi_lun=$iscsi_lun . /bin/mount-lun.sh " > "$hookdir"/mount/01-$$-iscsi.sh
+ fi
+
+ if strglobin "$iscsi_target_ip" '*:*:*' && ! strglobin "$iscsi_target_ip" '['; then
+ iscsi_target_ip="[$iscsi_target_ip]"
+ fi
+ targets=$(iscsiadm -m discovery -t st -p "$iscsi_target_ip":${iscsi_target_port:+$iscsi_target_port} | {
+ while read -r _ target _ || [ -n "$target" ]; do
+ echo "$target"
+ done
+ })
+ [ -z "$targets" ] && warn "Target discovery to $iscsi_target_ip:${iscsi_target_port:+$iscsi_target_port} failed with status $?" && return 1
+
+ found=
+ for target in $targets; do
+ if [ "$target" = "$iscsi_target_name" ]; then
+ if [ -n "$iscsi_iface_name" ]; then
+ iscsiadm -m iface -I "$iscsi_iface_name" --op=new
+ EXTRA=" ${iscsi_netdev_name:+--name=iface.net_ifacename --value=$iscsi_netdev_name} "
+ EXTRA="$EXTRA ${iscsi_initiator:+--name=iface.initiatorname --value=$iscsi_initiator} "
+ fi
+ [ -n "$iscsi_param" ] && for param in $iscsi_param; do EXTRA="$EXTRA --name=${param%=*} --value=${param#*=}"; done
+
+ CMD="iscsiadm -m node -T $target \
+ ${iscsi_iface_name:+-I $iscsi_iface_name} \
+ -p $iscsi_target_ip${iscsi_target_port:+:$iscsi_target_port}"
+ __op="--op=update \
+ --name=node.startup --value=onboot \
+ ${iscsi_username:+ --name=node.session.auth.username --value=$iscsi_username} \
+ ${iscsi_password:+ --name=node.session.auth.password --value=$iscsi_password} \
+ ${iscsi_in_username:+--name=node.session.auth.username_in --value=$iscsi_in_username} \
+ ${iscsi_in_password:+--name=node.session.auth.password_in --value=$iscsi_in_password} \
+ $EXTRA"
+ # shellcheck disable=SC2086
+ $CMD $__op
+ if [ "$netif" != "timeout" ]; then
+ $CMD --login
+ fi
+ found=yes
+ break
+ fi
+ done
+
+ if [ "$netif" = "timeout" ]; then
+ iscsiadm -m node -L onboot || :
+ elif [ "$found" != yes ]; then
+ warn "iSCSI target \"$iscsi_target_name\" not found on portal $iscsi_target_ip:$iscsi_target_port"
+ return 1
+ fi
+ : > "$hookdir"/initqueue/work
+
+ netroot_enc=$(str_replace "$1" '/' '\2f')
+ echo 'started' > "/tmp/iscsistarted-iscsi:${netroot_enc}"
+ return 0
+}
+
+ret=0
+
+if [ "$netif" != "timeout" ] && getargbool 0 rd.iscsi.waitnet; then
+ all_ifaces_setup || exit 0
+fi
+
+if [ "$netif" = "timeout" ] && all_ifaces_setup; then
+ # s.th. went wrong and the timeout script hits
+ # restart
+ systemctl restart iscsid
+ # damn iscsid is not ready after unit says it's ready
+ sleep 2
+fi
+
+if getargbool 0 rd.iscsi.firmware -d -y iscsi_firmware; then
+ if [ "$netif" = "timeout" ] || [ "$netif" = "online" ] || [ "$netif" = "dummy" ]; then
+ [ -f /tmp/session-retry ] || echo 1 > /tmp/session-retry
+ handle_firmware
+ ret=$?
+ fi
+fi
+
+if ! [ "$netif" = "online" ]; then
+ # loop over all netroot parameter
+ if nroot=$(getarg netroot) && [ "$nroot" != "dhcp" ]; then
+ for nroot in $(getargs netroot); do
+ [ "${nroot%%:*}" = "iscsi" ] || continue
+ nroot="${nroot##iscsi:}"
+ if [ -n "$nroot" ]; then
+ handle_netroot "$nroot"
+ ret=$((ret + $?))
+ fi
+ done
+ else
+ if [ -n "$iroot" ]; then
+ handle_netroot "$iroot"
+ ret=$?
+ fi
+ fi
+fi
+
+need_shutdown
+
+# now we have a root filesystem somewhere in /dev/sd*
+# let the normal block handler handle root=
+exit $ret
diff --git a/modules.d/95iscsi/module-setup.sh b/modules.d/95iscsi/module-setup.sh
new file mode 100755
index 0000000..2bea2fc
--- /dev/null
+++ b/modules.d/95iscsi/module-setup.sh
@@ -0,0 +1,294 @@
+#!/bin/bash
+
+# called by dracut
+check() {
+ # If our prerequisites are not met, fail anyways.
+ require_binaries iscsi-iname iscsiadm iscsid || return 1
+ require_kernel_modules iscsi_tcp || return 1
+
+ # If hostonly was requested, fail the check if we are not actually
+ # booting from root.
+ [[ $hostonly ]] || [[ $mount_needs ]] && {
+ pushd . > /dev/null
+ for_each_host_dev_and_slaves block_is_iscsi
+ local _is_iscsi=$?
+ popd > /dev/null || exit
+ [[ $_is_iscsi == 0 ]] || return 255
+ }
+ return 0
+}
+
+get_ibft_mod() {
+ local ibft_mac=$1
+ local iface_mac iface_mod
+ # Return the iSCSI offload module for a given MAC address
+ for iface_desc in $(iscsiadm -m iface | cut -f 2 -d ' '); do
+ iface_mod=${iface_desc%%,*}
+ iface_mac=${iface_desc#*,}
+ iface_mac=${iface_mac%%,*}
+ if [ "$ibft_mac" = "$iface_mac" ]; then
+ echo "$iface_mod"
+ return 0
+ fi
+ done
+}
+
+install_ibft() {
+ # When iBFT / iscsi_boot is detected:
+ # - Use 'ip=ibft' to set up iBFT network interface
+ # Note: bnx2i is using a different MAC address of iSCSI offloading
+ # so the 'ip=ibft' parameter must not be set
+ # - specify firmware booting cmdline parameter
+
+ for d in /sys/firmware/*; do
+ if [ -d "${d}"/ethernet0 ]; then
+ read -r ibft_mac < "${d}"/ethernet0/mac
+ ibft_mod=$(get_ibft_mod "$ibft_mac")
+ fi
+ if [ -z "$ibft_mod" ] && [ -d "${d}"/ethernet1 ]; then
+ read -r ibft_mac < "${d}"/ethernet1/mac
+ ibft_mod=$(get_ibft_mod "$ibft_mac")
+ fi
+ if [ -d "${d}"/initiator ]; then
+ if [ "${d##*/}" = "ibft" ] && [ "$ibft_mod" != "bnx2i" ]; then
+ echo -n "rd.iscsi.ibft=1 "
+ fi
+ echo -n "rd.iscsi.firmware=1 "
+ fi
+ done
+}
+
+install_iscsiroot() {
+ local devpath=$1
+ local scsi_path iscsi_lun session c d conn host flash
+ local iscsi_session iscsi_address iscsi_port iscsi_targetname
+
+ scsi_path=${devpath%%/block*}
+ [ "$scsi_path" = "$devpath" ] && return 1
+ iscsi_lun=${scsi_path##*:}
+ [ "$iscsi_lun" = "$scsi_path" ] && return 1
+ session=${devpath%%/target*}
+ [ "$session" = "$devpath" ] && return 1
+ iscsi_session=${session##*/}
+ [ "$iscsi_session" = "$session" ] && return 1
+ host=${session%%/session*}
+ [ "$host" = "$session" ] && return 1
+ iscsi_host=${host##*/}
+
+ for flash in "${host}"/flashnode_sess-*; do
+ [ -f "$flash" ] || continue
+ [ ! -e "$flash/is_boot_target" ] && continue
+ read -r is_boot < "$flash"/is_boot_target
+ if [ "$is_boot" -eq 1 ]; then
+ # qla4xxx flashnode session; skip iBFT discovery
+ read -r iscsi_initiator < /sys/class/iscsi_host/"${iscsi_host}"/initiatorname
+ echo "rd.iscsi.initiator=${iscsi_initiator}"
+ return
+ fi
+ done
+
+ for d in "${session}"/*; do
+ case $d in
+ *connection*)
+ c=${d##*/}
+ conn=${d}/iscsi_connection/${c}
+ if [ -d "${conn}" ]; then
+ read -r iscsi_address < "${conn}"/persistent_address
+ read -r iscsi_port < "${conn}"/persistent_port
+ fi
+ ;;
+ *session)
+ if [ -d "${d}"/"${iscsi_session}" ]; then
+ read -r iscsi_initiator < "${d}"/"${iscsi_session}"/initiatorname
+ read -r iscsi_targetname < "${d}"/"${iscsi_session}"/targetname
+ fi
+ ;;
+ esac
+ done
+
+ [ -z "$iscsi_address" ] && return
+ ip_params_for_remote_addr "$iscsi_address"
+
+ if [ -n "$iscsi_address" -a -n "$iscsi_targetname" ]; then
+ if [ -n "$iscsi_port" -a "$iscsi_port" -eq 3260 ]; then
+ iscsi_port=
+ fi
+ if [ -n "$iscsi_lun" -a "$iscsi_lun" -eq 0 ]; then
+ iscsi_lun=
+ fi
+ # In IPv6 case rd.iscsi.initatior= must pass address in [] brackets
+ case "$iscsi_address" in
+ *:*)
+ iscsi_address="[$iscsi_address]"
+ ;;
+ esac
+ # Must be two separate lines, so that "sort | uniq" commands later
+ # can sort out rd.iscsi.initiator= duplicates
+ echo "rd.iscsi.initiator=${iscsi_initiator}"
+ echo "netroot=iscsi:${iscsi_address}::${iscsi_port}:${iscsi_lun}:${iscsi_targetname}"
+ echo "rd.neednet=1"
+ fi
+ return 0
+}
+
+install_softiscsi() {
+ [ -d /sys/firmware/ibft ] && return 0
+
+ is_softiscsi() {
+ local _dev=$1
+ local iscsi_dev
+
+ [[ -L "/sys/dev/block/$_dev" ]] || return
+ iscsi_dev=$(
+ cd -P /sys/dev/block/"$_dev" || exit
+ echo "$PWD"
+ )
+ install_iscsiroot "$iscsi_dev"
+ }
+
+ for_each_host_dev_and_slaves_all is_softiscsi || return 255
+ return 0
+}
+
+# called by dracut
+depends() {
+ echo network rootfs-block
+}
+
+# called by dracut
+installkernel() {
+ local _arch=${DRACUT_ARCH:-$(uname -m)}
+ local _funcs='iscsi_register_transport'
+
+ instmods bnx2i qla4xxx cxgb3i cxgb4i be2iscsi qedi
+ hostonly="" instmods iscsi_tcp iscsi_ibft crc32c iscsi_boot_sysfs 8021q
+
+ if [ "$_arch" = "s390" -o "$_arch" = "s390x" ]; then
+ _s390drivers="=drivers/s390/scsi"
+ fi
+
+ dracut_instmods -o -s ${_funcs} =drivers/scsi ${_s390drivers:+"$_s390drivers"}
+}
+
+# called by dracut
+cmdline() {
+ local _iscsiconf
+ _iscsiconf=$(install_ibft)
+ {
+ if [ "$_iscsiconf" ]; then
+ echo "${_iscsiconf}"
+ else
+ install_softiscsi
+ fi
+ } | sort | uniq
+}
+
+# called by dracut
+install() {
+ inst_multiple -o iscsiuio
+ inst_libdir_file 'libgcc_s.so*'
+ inst_multiple umount iscsi-iname iscsiadm iscsid
+ inst_binary sort
+
+ inst_multiple -o \
+ "$systemdsystemunitdir"/iscsid.socket \
+ "$systemdsystemunitdir"/iscsid.service \
+ "$systemdsystemunitdir"/iscsiuio.service \
+ "$systemdsystemunitdir"/iscsiuio.socket \
+ "$systemdsystemunitdir"/sockets.target.wants/iscsid.socket \
+ "$systemdsystemunitdir"/sockets.target.wants/iscsiuio.socket
+
+ if [[ $hostonly ]]; then
+ local -a _filenames
+
+ inst_dir /etc/iscsi
+ mapfile -t -d '' _filenames < <(find /etc/iscsi -type f -print0)
+ inst_multiple "${_filenames[@]}"
+ else
+ inst_simple /etc/iscsi/iscsid.conf
+ fi
+
+ # Detect iBFT and perform mandatory steps
+ if [[ $hostonly_cmdline == "yes" ]]; then
+ local _iscsiconf
+ _iscsiconf=$(cmdline)
+ [[ $_iscsiconf ]] && printf "%s\n" "$_iscsiconf" >> "${initdir}/etc/cmdline.d/95iscsi.conf"
+ fi
+
+ inst_hook cmdline 90 "$moddir/parse-iscsiroot.sh"
+ inst_hook cleanup 90 "$moddir/cleanup-iscsi.sh"
+ inst "$moddir/iscsiroot.sh" "/sbin/iscsiroot"
+
+ if ! dracut_module_included "systemd"; then
+ inst "$moddir/mount-lun.sh" "/bin/mount-lun.sh"
+ else
+ inst_multiple -o \
+ "$systemdsystemunitdir"/iscsi.service \
+ "$systemdsystemunitdir"/iscsi-init.service \
+ "$systemdsystemunitdir"/iscsid.service \
+ "$systemdsystemunitdir"/iscsid.socket \
+ "$systemdsystemunitdir"/iscsiuio.service \
+ "$systemdsystemunitdir"/iscsiuio.socket \
+ iscsiadm iscsid
+
+ for i in \
+ iscsid.socket \
+ iscsiuio.socket; do
+ $SYSTEMCTL -q --root "$initdir" enable "$i"
+ done
+
+ mkdir -p "${initdir}/$systemdsystemunitdir/iscsid.service.d"
+ {
+ echo "[Unit]"
+ echo "DefaultDependencies=no"
+ echo "Conflicts=shutdown.target"
+ echo "Before=shutdown.target"
+ } > "${initdir}/$systemdsystemunitdir/iscsid.service.d/dracut.conf"
+
+ mkdir -p "${initdir}/$systemdsystemunitdir/iscsid.socket.d"
+ {
+ echo "[Unit]"
+ echo "DefaultDependencies=no"
+ echo "Conflicts=shutdown.target"
+ echo "Before=shutdown.target sockets.target"
+ } > "${initdir}/$systemdsystemunitdir/iscsid.socket.d/dracut.conf"
+
+ mkdir -p "${initdir}/$systemdsystemunitdir/iscsiuio.service.d"
+ {
+ echo "[Unit]"
+ echo "DefaultDependencies=no"
+ echo "Conflicts=shutdown.target"
+ echo "Before=shutdown.target"
+ } > "${initdir}/$systemdsystemunitdir/iscsiuio.service.d/dracut.conf"
+
+ mkdir -p "${initdir}/$systemdsystemunitdir/iscsiuio.socket.d"
+ {
+ echo "[Unit]"
+ echo "DefaultDependencies=no"
+ echo "Conflicts=shutdown.target"
+ echo "Before=shutdown.target sockets.target"
+ } > "${initdir}/$systemdsystemunitdir/iscsiuio.socket.d/dracut.conf"
+
+ # Fedora 34 iscsid requires iscsi-shutdown.service
+ # which would terminate all iSCSI connections on switch root
+ cat > "${initdir}/$systemdsystemunitdir/iscsi-shutdown.service" << EOF
+[Unit]
+Description=Dummy iscsi-shutdown.service for the initrd
+Documentation=man:iscsid(8) man:iscsiadm(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=systemd-remount-fs.service network.target iscsid.service iscsiuio.service
+Before=remote-fs-pre.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=false
+ExecStart=-/usr/bin/true
+EOF
+ fi
+ inst_dir /var/lib/iscsi
+ mkdir -p "${initdir}/var/lib/iscsi/nodes"
+ # Fedora 34 iscsid wants a non-empty /var/lib/iscsi/nodes directory
+ : > "${initdir}/var/lib/iscsi/nodes/.dracut"
+ dracut_need_initqueue
+}
diff --git a/modules.d/95iscsi/mount-lun.sh b/modules.d/95iscsi/mount-lun.sh
new file mode 100755
index 0000000..c186984
--- /dev/null
+++ b/modules.d/95iscsi/mount-lun.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+if [ -z "$iscsi_lun" ]; then
+ iscsi_lun=0
+fi
+NEWROOT=${NEWROOT:-/sysroot}
+
+for disk in /dev/disk/by-path/*-iscsi-*-"$iscsi_lun"; do
+ if mount -t "${fstype:-auto}" -o "$rflags" "$disk" "$NEWROOT"; then
+ if [ ! -d "$NEWROOT"/proc ]; then
+ umount "$disk"
+ continue
+ fi
+ break
+ fi
+done
diff --git a/modules.d/95iscsi/parse-iscsiroot.sh b/modules.d/95iscsi/parse-iscsiroot.sh
new file mode 100755
index 0000000..7574711
--- /dev/null
+++ b/modules.d/95iscsi/parse-iscsiroot.sh
@@ -0,0 +1,156 @@
+#!/bin/sh
+#
+# Preferred format:
+# root=iscsi:[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
+# [root=*] netroot=iscsi:[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
+#
+# Legacy formats:
+# [net]root=[iscsi] iscsiroot=[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
+# [net]root=[iscsi] iscsi_firmware
+#
+# root= takes precedence over netroot= if root=iscsi[...]
+#
+
+# This script is sourced, so root should be set. But let's be paranoid
+[ -z "$root" ] && root=$(getarg root=)
+if [ -z "$netroot" ]; then
+ for nroot in $(getargs netroot=); do
+ [ "${nroot%%:*}" = "iscsi" ] && break
+ done
+ if [ "${nroot%%:*}" = "iscsi" ]; then
+ netroot="$nroot"
+ else
+ for nroot in $(getargs netroot=); do
+ [ "${nroot%%:*}" = "dhcp" ] && break
+ done
+ netroot="$nroot"
+ fi
+fi
+[ -z "$iscsiroot" ] && iscsiroot=$(getarg iscsiroot=)
+[ -z "$iscsi_firmware" ] && getargbool 0 rd.iscsi.firmware -y iscsi_firmware && iscsi_firmware="1"
+
+[ -n "$iscsiroot" ] && [ -n "$iscsi_firmware" ] && die "Mixing iscsiroot and iscsi_firmware is dangerous"
+
+type write_fs_tab > /dev/null 2>&1 || . /lib/fs-lib.sh
+
+# Root takes precedence over netroot
+if [ "${root%%:*}" = "iscsi" ]; then
+ if [ -n "$netroot" ]; then
+ echo "Warning: root takes precedence over netroot. Ignoring netroot"
+ fi
+ netroot=$root
+ # if root is not specified try to mount the whole iSCSI LUN
+ printf 'ENV{DEVTYPE}!="partition", SYMLINK=="disk/by-path/*-iscsi-*-*", SYMLINK+="root"\n' >> /etc/udev/rules.d/99-iscsi-root.rules
+ [ -n "$DRACUT_SYSTEMD" ] && systemctl is-active systemd-udevd && udevadm control --reload-rules
+ root=/dev/root
+
+ write_fs_tab /dev/root
+fi
+
+# If it's not empty or iscsi we don't continue
+for nroot in $(getargs netroot); do
+ [ "${nroot%%:*}" = "iscsi" ] || continue
+ netroot="$nroot"
+ break
+done
+
+# Root takes precedence over netroot
+if [ "${root}" = "/dev/root" ] && getarg "netroot=dhcp"; then
+ # if root is not specified try to mount the whole iSCSI LUN
+ printf 'ENV{DEVTYPE}!="partition", SYMLINK=="disk/by-path/*-iscsi-*-*", SYMLINK+="root"\n' >> /etc/udev/rules.d/99-iscsi-root.rules
+ [ -n "$DRACUT_SYSTEMD" ] && systemctl is-active systemd-udevd && udevadm control --reload-rules
+fi
+
+if [ -n "$iscsiroot" ]; then
+ [ -z "$netroot" ] && netroot=$root
+
+ # @deprecated
+ echo "Warning: Argument iscsiroot is deprecated and might be removed in a future"
+ echo "release. See 'man dracut.kernel' for more information."
+
+ # Accept iscsiroot argument?
+ [ -z "$netroot" ] || [ "$netroot" = "iscsi" ] \
+ || die "Argument iscsiroot only accepted for empty root= or [net]root=iscsi"
+
+ # Override root with iscsiroot content?
+ [ -z "$netroot" ] || [ "$netroot" = "iscsi" ] && netroot=iscsi:$iscsiroot
+fi
+
+# iscsi_firmware does not need argument checking
+if [ -n "$iscsi_firmware" ]; then
+ if [ "$root" != "dhcp" ] && [ "$netroot" != "dhcp" ]; then
+ [ -z "$netroot" ] && netroot=iscsi:
+ fi
+ modprobe -b -q iscsi_boot_sysfs 2> /dev/null
+ modprobe -b -q iscsi_ibft
+ # if no ip= is given, but firmware
+ echo "${DRACUT_SYSTEMD+systemctl is-active initrd-root-device.target || }[ -f '/tmp/iscsistarted-firmware' ]" > "$hookdir"/initqueue/finished/iscsi_started.sh
+ initqueue --unique --online /sbin/iscsiroot online "iscsi:" "$NEWROOT"
+ initqueue --unique --onetime --timeout /sbin/iscsiroot timeout "iscsi:" "$NEWROOT"
+ initqueue --unique --onetime --settled /sbin/iscsiroot online "iscsi:" "'$NEWROOT'"
+fi
+
+# ISCSI actually supported?
+if ! [ -e /sys/module/iscsi_tcp ]; then
+ modprobe -b -q iscsi_tcp || die "iscsiroot requested but kernel/initrd does not support iscsi"
+fi
+
+modprobe --all -b -q qla4xxx cxgb3i cxgb4i bnx2i be2iscsi
+
+if [ -n "$netroot" ] && [ "$root" != "/dev/root" ] && [ "$root" != "dhcp" ]; then
+ if ! getargbool 1 rd.neednet > /dev/null || ! getarg "ip="; then
+ initqueue --unique --onetime --settled /sbin/iscsiroot dummy "'$netroot'" "'$NEWROOT'"
+ fi
+fi
+
+if arg=$(getarg rd.iscsi.initiator -d iscsi_initiator=) && [ -n "$arg" ] && ! [ -f /run/initiatorname.iscsi ]; then
+ iscsi_initiator=$arg
+ echo "InitiatorName=$iscsi_initiator" > /run/initiatorname.iscsi
+ ln -fs /run/initiatorname.iscsi /dev/.initiatorname.iscsi
+ rm -f /etc/iscsi/initiatorname.iscsi
+ mkdir -p /etc/iscsi
+ ln -fs /run/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi
+ if [ -n "$DRACUT_SYSTEMD" ]; then
+ systemctl try-restart iscsid
+ # FIXME: iscsid is not yet ready, when the service is :-/
+ sleep 1
+ fi
+fi
+
+# If not given on the cmdline and initiator-name available via iBFT
+if [ -z "$iscsi_initiator" ] && [ -f /sys/firmware/ibft/initiator/initiator-name ] && ! [ -f /tmp/iscsi_set_initiator ]; then
+ iscsi_initiator=$(while read -r line || [ -n "$line" ]; do echo "$line"; done < /sys/firmware/ibft/initiator/initiator-name)
+ if [ -n "$iscsi_initiator" ]; then
+ echo "InitiatorName=$iscsi_initiator" > /run/initiatorname.iscsi
+ rm -f /etc/iscsi/initiatorname.iscsi
+ mkdir -p /etc/iscsi
+ ln -fs /run/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi
+ : > /tmp/iscsi_set_initiator
+ if [ -n "$DRACUT_SYSTEMD" ]; then
+ systemctl try-restart iscsid
+ # FIXME: iscsid is not yet ready, when the service is :-/
+ sleep 1
+ fi
+ fi
+fi
+
+if [ -z "$netroot" ] || ! [ "${netroot%%:*}" = "iscsi" ]; then
+ return 1
+fi
+
+initqueue --unique --onetime --timeout /sbin/iscsiroot timeout "$netroot" "$NEWROOT"
+
+for nroot in $(getargs netroot); do
+ [ "${nroot%%:*}" = "iscsi" ] || continue
+ type parse_iscsi_root > /dev/null 2>&1 || . /lib/net-lib.sh
+ parse_iscsi_root "$nroot" || return 1
+ netroot_enc=$(str_replace "$nroot" '/' '\2f')
+ echo "${DRACUT_SYSTEMD+systemctl is-active initrd-root-device.target || }[ -f '/tmp/iscsistarted-$netroot_enc' ]" > "$hookdir"/initqueue/finished/iscsi_started.sh
+done
+
+# Done, all good!
+# shellcheck disable=SC2034
+rootok=1
+
+# Shut up init error check
+[ -z "$root" ] && root="iscsi"