]> git.ipfire.org Git - thirdparty/dracut.git/blobdiff - modules.d/95iscsi/module-setup.sh
feat(dracut.sh): allow overriding the systemctl command for sysroot
[thirdparty/dracut.git] / modules.d / 95iscsi / module-setup.sh
index a001a2896a781b8a79add7ce798b5130f1b52a28..8bd86d0c7f3c569954fe6c40f0638b4a95422f16 100755 (executable)
 #!/bin/bash
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
 
+# called by dracut
 check() {
     local _rootdev
     # If our prerequisites are not met, fail anyways.
-    type -P iscsistart hostname iscsi-iname >/dev/null || return 1
+    require_binaries iscsi-iname iscsiadm iscsid || return 1
 
     # If hostonly was requested, fail the check if we are not actually
     # booting from root.
 
-    . $dracutfunctions
+    [[ $hostonly ]] || [[ $mount_needs ]] && {
+        pushd . >/dev/null
+        for_each_host_dev_and_slaves block_is_iscsi
+        local _is_iscsi=$?
+        popd >/dev/null
+        [[ $_is_iscsi == 0 ]] || return 255
+    }
+    return 0
+}
 
-    [[ $debug ]] && set -x
+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
+}
 
-    is_iscsi() (
-        [[ -L /sys/dev/block/$1 ]] || return
-        cd "$(readlink -f /sys/dev/block/$1)"
-        until [[ -d sys || -d iscsi_session ]]; do
-            cd ..
-        done
-        [[ -d iscsi_session ]]
-    )
+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
 
-    [[ $hostonly ]] || [[ $mount_needs ]] && {
-        _rootdev=$(find_root_block_device)
-        if [[ $_rootdev ]]; then
-            # root lives on a block device, so we can be more precise about
-            # hostonly checking
-            check_block_and_slaves is_iscsi "$_rootdev" || return 1
-        else
-            return 1
+    for d in /sys/firmware/* ; do
+        if [ -d ${d}/ethernet0 ] ; then
+            read ibft_mac < ${d}/ethernet0/mac
+            ibft_mod=$(get_ibft_mod $ibft_mac)
+        fi
+        if [ -z "$ibft_mod" ] && [ -d ${d}/ethernet1 ] ; then
+            read 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 iscsi_tpgt
+    local bootproto
+
+    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
+        is_boot=$(cat $flash/is_boot_target)
+        if [ $is_boot -eq 1 ] ; then
+            # qla4xxx flashnode session; skip iBFT discovery
+            iscsi_initiator=$(cat /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
+                   iscsi_address=$(cat ${conn}/persistent_address)
+                   iscsi_port=$(cat ${conn}/persistent_port)
+               fi
+               ;;
+           *session)
+               if [ -d ${d}/${iscsi_session} ] ; then
+                    iscsi_initiator=$(cat ${d}/${iscsi_session}/initiatorname)
+                   iscsi_targetname=$(cat ${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; 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() {
-    instmods iscsi_tcp iscsi_ibft crc32c bnx2i iscsi_boot_sysfs qla4xxx cxgb3i cxgb4i be2iscsi
-    iscsi_module_filter() {
-        local _iscsifuncs='iscsi_register_transport'
-        local _f
-        while read _f; do case "$_f" in
-            *.ko)    [[ $(<         $_f) =~ $_iscsifuncs ]] && echo "$_f" ;;
-            *.ko.gz) [[ $(gzip -dc <$_f) =~ $_iscsifuncs ]] && echo "$_f" ;;
-            *.ko.xz) [[ $(xz -dc   <$_f) =~ $_iscsifuncs ]] && echo "$_f" ;;
-            esac
-        done
-    }
-    { find_kernel_modules_by_path drivers/scsi; find_kernel_modules_by_path drivers/s390/scsi; } \
-    | iscsi_module_filter  |  instmods
+    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
+
+    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=$(install_ibft)
+    {
+        if [ "$_iscsiconf" ] ; then
+            echo ${_iscsiconf}
+        else
+            install_softiscsi
+        fi
+    } | sort | uniq
 }
 
+# called by dracut
 install() {
-    dracut_install umount
-    dracut_install -o iscsiuio
-    inst iscsistart
-    inst hostname
-    inst iscsi-iname
+    inst_multiple -o iscsiuio
+    inst_libdir_file 'libgcc_s.so*'
+    inst_multiple umount iscsi-iname iscsiadm iscsid
+
+    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
+        inst_dir /etc/iscsi
+        inst_multiple $(find /etc/iscsi -type f)
+    else
+        inst_simple /etc/iscsi/iscsid.conf
+    fi
+
+    # Detect iBFT and perform mandatory steps
+    if [[ $hostonly_cmdline == "yes" ]] ; then
+        local _iscsiconf=$(cmdline)
+        [[ $_iscsiconf ]] && printf "%s\n" "$_iscsiconf" >> "${initdir}/etc/cmdline.d/95iscsi.conf"
+    fi
+
     inst_hook cmdline 90 "$moddir/parse-iscsiroot.sh"
-    inst_hook pre-pivot-cleanup 90 "$moddir/cleanup-iscsi.sh"
+    inst_hook cleanup 90 "$moddir/cleanup-iscsi.sh"
     inst "$moddir/iscsiroot.sh" "/sbin/iscsiroot"
-    inst "$moddir/mount-lun.sh" "/bin/mount-lun.sh"
+
+    if ! dracut_module_included "systemd"; then
+        inst "$moddir/mount-lun.sh" "/bin/mount-lun.sh"
+    else
+        inst_multiple -o \
+                      $systemdsystemunitdir/iscsi.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
+        
+        for i in \
+                iscsid.service \
+                iscsiuio.service \
+            ; do
+            $SYSTEMCTL -q --root "$initdir" add-wants basic.target "$i"
+        done
+
+        # Make sure iscsid is started after dracut-cmdline and ready for the initqueue
+        mkdir -p "${initdir}/$systemdsystemunitdir/iscsid.service.d"
+        (
+            echo "[Unit]"
+            echo "After=dracut-cmdline.service"
+            echo "Before=dracut-initqueue.service"
+        ) > "${initdir}/$systemdsystemunitdir/iscsid.service.d/dracut.conf"
+
+        # The iscsi deamon does not need to wait for any storage inside initrd
+        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.socket.d"
+        (
+            echo "[Unit]"
+            echo "DefaultDependencies=no"
+            echo "Conflicts=shutdown.target"
+            echo "Before=shutdown.target sockets.target"
+        ) > "${initdir}/$systemdsystemunitdir/iscsiuio.socket.d/dracut.conf"
+
+    fi
+    inst_dir /var/lib/iscsi
+    dracut_need_initqueue
 }