diff options
Diffstat (limited to 'defaults/initrd.scripts')
-rw-r--r-- | defaults/initrd.scripts | 2027 |
1 files changed, 1441 insertions, 586 deletions
diff --git a/defaults/initrd.scripts b/defaults/initrd.scripts index bc19fff3..506f8c6c 100644 --- a/defaults/initrd.scripts +++ b/defaults/initrd.scripts @@ -29,49 +29,117 @@ modules_load() { modules_scan() { local MODS local loaded + local x + local smart_loading=yes + local _root_dev + local root_dev_found="Root block device found, skipping loading of module group \"${1}\" ..." MODS=$(cat /etc/modules/${1} 2>/dev/null) - [ -n "${MODS}" ] && [ -z "${QUIET}" ] && \ - printf "%b" "${BOLD} ::${NORMAL} Loading from ${1}: " + if [ -z "${MODS}" ] + then + log_msg "/etc/modules/${1} is empty; Nothing to load for '${1}' ..." + return + fi + + if [ -z "${MODULES_SCAN_WARNING_SHOWN}" ] + then + local note_msg="NOTE: Due to how genkernel auto-detects your" + note_msg="${note_msg} hardware you will now see a lot of failed modprobe" \ + note_msg="${note_msg} attempts which you can ignore:" + + log_msg "${note_msg}" + + MODULES_SCAN_WARNING_SHOWN=yes + fi + + if [ "${GK_HW_LOAD_ALL_MODULES}" = '1' ] + then + smart_loading=no + elif [ "${1}" = "virtio" ] || [ "${1}" = "hyperv" ] + then + # Virtio/HyperV modules group is special -- it's about + # hypervisor support in general, not root block device + smart_loading=no + elif [ "${1}" = "net" ] + then + # We already load network modules only when we need + # network so don't stop loading network modules when + # $REAL_ROOT is already present or we will probably + # end up without network we wanted ... + smart_loading=no + elif [ "${1}" = "fs" ] + then + # We don't know if kernel supports root filesystem so + # better load all filesystems ... + smart_loading=no + elif [ "${USE_MDADM}" = '1' ] \ + || [ "${USE_LVM_NORMAL}" = '1' ] \ + || [ "${USE_CRYPTSETUP}" = '1' ] \ + || [ "${USE_ZFS}" = '1' ] \ + || [ "${USE_DMRAID_NORMAL}" = '1' ] + then + # All of this will require the call of another program before + # root becomes available so checking for root after each module + # was loaded will only waste time. + smart_loading=no + fi + + log_msg "Loading modules of module group '${1}' (smart loading: ${smart_loading}) ..." + is_quiet \ + || printf "%b" "${BOLD} ::${NORMAL} Loading from ${1}: " for x in ${MODS} do MLOAD=$(echo ${MLIST} | sed -e "s/.*${x}.*/${x}/") if [ "${MLOAD}" = "${x}" ] # Only module to no-load then - [ -z "${QUIET}" ] && \ - printf "%b\n" "${BOLD} ::${NORMAL} Skipping ${x} ..." + is_quiet \ + || printf "%b\n" "${BOLD} ::${NORMAL} Skipping ${x} ..." elif [ "${MLOAD}" = "${MLIST}" ] then - if [ "${ROOTFSTYPE}" != 'auto' ] && [ -b "${REAL_ROOT}" ] + if [ "${smart_loading}" = "yes" ] then - echo "Root block device found, continuing ..." - break + if [ ! -f "${GK_ROOT_DEV_DETECTED_STATEFILE}" ] + then + _root_dev=$(findfs "${REAL_ROOT}" 2>/dev/null) + if [ $? -eq 0 ] && [ -n "${_root_dev}" ] && [ -b "${_root_dev}" ] + then + echo "${_root_dev}" > "${GK_ROOT_DEV_DETECTED_STATEFILE}" + fi + fi + + if [ -f "${GK_ROOT_DEV_DETECTED_STATEFILE}" ] + then + log_msg "${root_dev_found}" + printf "%b" "${root_dev_found}" + break + fi fi - if [ -n "${DEBUG}" ] + if is_debug then printf "%b" "${BOLD} ::${NORMAL} " printf "%b" "Scanning for ${x} ..." fi - modprobe ${x} >/dev/null 2>&1 + run modprobe ${x} >/dev/null 2>&1 loaded=${?} - [ -n "${DEBUG}" -a "${loaded}" = "0" ] && \ + is_debug && [ "${loaded}" = "0" ] && \ echo "loaded" - [ -n "${DEBUG}" -a "${loaded}" != "0" ] && \ + is_debug && [ "${loaded}" != "0" ] && \ echo "not loaded" - [ -z "${DEBUG}" -a "${loaded}" = "0" ] && \ - [ -z "${QUIET}" ] && \ + ! is_debug && [ "${loaded}" = "0" ] && \ + ! is_quiet && \ printf "%b" "${x} " else - [ -z "${QUIET}" ] && \ - printf "%b\n" "${BOLD} ::${NORMAL} Skipping ${x} ..." + is_quiet \ + || printf "%b\n" "${BOLD} ::${NORMAL} Skipping ${x} ..." fi done - [ -n "${MODS}" ] && [ -z "${QUIET}" ] && echo + + is_quiet || echo } uppercase() { @@ -79,7 +147,6 @@ uppercase() { echo $1 | tr 'a-z' 'A-Z' } - findmediamount() { # $1 = mount dir name / media name # $2 = recognition file @@ -111,33 +178,18 @@ findmediamount() { # Check for a block device to mount if [ -b "${x}" ] then - skip=0 - bsn=$(basename "${x}") - # - # If disk and it has at least one partition, skip. - # We use /sys/block/${bsn}/${bsn}[0-9]* to make sure that we - # don't skip device mapper devices. Even the craziest scenario - # deserves a fair chance. - # - for part in $(ls /sys/block/${bsn}/${bsn}*[0-9]* 2>/dev/null) - do - skip=1 - break; - done - if [ ${skip} -eq 1 ] - then - continue - fi good_msg "Attempting to mount media: ${x}" ${CRYPT_SILENT} - mount -t ${CDROOT_TYPE} ${x} ${mntcddir} >/dev/null 2>&1 + CDROOT_TYPE=$(determine_fs "${x}" "${CDROOT_TYPE}") + + run mount -t ${CDROOT_TYPE} ${x} ${mntcddir} >/dev/null 2>&1 if [ $? -eq 0 ] then if [ -n "${ISOBOOT}" ] then if [ -f "${mntcddir}/${ISOBOOT}" ] then - mount -o loop "${mntcddir}/${ISOBOOT}" "${mntdir}" + run mount -o loop "${mntcddir}/${ISOBOOT}" "${mntdir}" if [ $? -eq 0 ] then good_msg "iso mounted on ${mntdir}" @@ -153,7 +205,7 @@ findmediamount() { good_msg "Media found on ${x}" ${CRYPT_SILENT} break else - umount ${mntcddir} + run umount ${mntcddir} fi fi fi @@ -165,6 +217,31 @@ findmediamount() { [ -n "${result}" ] || bad_msg "Media not found" ${CRYPT_SILENT} } +determine_fs() { + local _dev="${1}" + local _orig="${2:-auto}" + local _fs line + + _fs=$(udevadm info --query=env --name="$_dev" 2>/dev/null | \ + while read line || [ -n "${line}" ] + do + if str_starts ${line} "ID_FS_TYPE=" + then + echo ${line#ID_FS_TYPE=} + break + fi + done + ) + _fs=${_fs:-auto} + + if [ "${_fs}" = "auto" ] + then + _fs="${_orig}" + fi + + echo "${_fs}" +} + devicelist() { # Locate the cdrom device with our media on it. # CDROM DEVICES @@ -203,37 +280,38 @@ bootstrapFS() { aufs_branch=${aufs_memory}/aufs-rw-branch/${aufs_dev_uid} fi - mkdir -p ${aufs_memory} ${aufs_union} ${aufs_dev_mnt} + run mkdir -p ${aufs_memory} ${aufs_union} ${aufs_dev_mnt} else # Legacy SquashFS implementation good_msg "Making tmpfs for ${NEW_ROOT}" - mount -n -t tmpfs tmpfs ${NEW_ROOT} + run mount -n -t tmpfs tmpfs ${NEW_ROOT} fi # Setup the filesystem nodes and directories - for i in ${CDROOT_PATH} /mnt/livecd /mnt/key /mnt/gentoo /tmp /tmp/.initrd /dev /proc /run /sys; do - mkdir -p "${NEW_ROOT}${i}" - chmod 755 "${NEW_ROOT}${i}" + for i in ${CDROOT_PATH} /mnt/header /mnt/livecd /mnt/key /mnt/gentoo /tmp /tmp/.initrd /dev /proc /run /sys; do + run mkdir -p "${NEW_ROOT}${i}" + run chmod 755 "${NEW_ROOT}${i}" done - [ ! -d "${CDROOT_PATH}" ] && mkdir -p "${CDROOT_PATH}" - [ ! -e "${NEW_ROOT}/dev/null" ] && mknod -m 666 "${NEW_ROOT}"/dev/null c 1 3 - [ ! -e "${NEW_ROOT}/dev/zero" ] && mknod -m 666 "${NEW_ROOT}"/dev/zero c 1 5 - [ ! -e "${NEW_ROOT}/dev/console" ] && mknod -m 600 "${NEW_ROOT}"/dev/console c 5 1 - [ ! -e "${NEW_ROOT}/dev/ttyS0" ] && mknod -m 660 "${NEW_ROOT}"/dev/ttyS0 c 4 64 + [ ! -d "${CDROOT_PATH}" ] && run mkdir -p "${CDROOT_PATH}" + [ ! -e "${NEW_ROOT}/dev/null" ] && run mknod -m 666 "${NEW_ROOT}"/dev/null c 1 3 + [ ! -e "${NEW_ROOT}/dev/zero" ] && run mknod -m 666 "${NEW_ROOT}"/dev/zero c 1 5 + [ ! -e "${NEW_ROOT}/dev/console" ] && run mknod -m 600 "${NEW_ROOT}"/dev/console c 5 1 + [ ! -e "${NEW_ROOT}/dev/tty0" ] && run mknod -m 620 "${NEW_ROOT}"/dev/tty0 c 4 0 + [ ! -e "${NEW_ROOT}/dev/ttyS0" ] && run mknod -m 660 "${NEW_ROOT}"/dev/ttyS0 c 4 64 # For SGI LiveCDs if [ "${LOOPTYPE}" = "sgimips" ] then - [ ! -e "${NEW_ROOT}/dev/sr0" ] && mknod "${NEW_ROOT}/dev/sr0" b 11 0 - [ ! -e "${NEW_ROOT}/dev/loop0" ] && mknod "${NEW_ROOT}/dev/loop0" b 7 0 + [ ! -e "${NEW_ROOT}/dev/sr0" ] && run mknod "${NEW_ROOT}/dev/sr0" b 11 0 + [ ! -e "${NEW_ROOT}/dev/loop0" ] && run mknod "${NEW_ROOT}/dev/loop0" b 7 0 fi # Required for splash to work. Not an issue with the initrd as this # device isn't created there and is not needed. for minor in 0 1 do - [ ! -e "${NEW_ROOT}/dev/${minor}" ] && mknod -m 600 "${NEW_ROOT}/dev/tty${minor}" c 4 ${minor} + [ ! -e "${NEW_ROOT}/dev/${minor}" ] && run mknod -m 600 "${NEW_ROOT}/dev/tty${minor}" c 4 ${minor} done } @@ -251,24 +329,58 @@ bootstrapCD() { if [ "${VERIFY}" = '1' ] then cd "${CDROOT_PATH}" - if [ -f isoroot_checksums ] + checkfile="" + checker="" + if [ -r "isoroot_checksums" ] && [ -z "${checkfile}" ] + then + checkfile="isoroot_checksums" + checker="sha512sum" + fi + if [ -r "isoroot_b2sums" ] then - good_msg "Verifying checksums, this may take some time ..." - if ! busybox sha512sum -c isoroot_checksums + if [ -x "$(command -v b2sum 2>&1)" ] + then + checkfile="isoroot_b2sums" + checker="b2sum" + else + bad_msg "Unable to verify isoroot_b2sums due to missing b2sums" + bad_msg "Please use 'genkernel --b2sums' to generate initramfs" + if [ -z "${checkfile}" ] + then + bad_msg "Press any key to skip ..." + read -n1 -s + return + fi + fi + fi + + if [ -n "${checkfile}" ] + then + good_msg "Verifying ${checkfile}, this may take some time ..." + if ! run "${checker}" -c "${checkfile}" then bad_msg "Some checksums failed, press any key to poweroff ..." read -n1 -s - busybox poweroff -f + poweroff -f else - good_msg "Checksums all valid, continuing boot ..." + good_msg "${checkfile} all valid, continuing boot ..." fi cd "${OLDPWD}" else - bad_msg "Verify enabled but no checksums file exists, skipping" + bad_msg "Verify requested but no checksums file exists, press any key to skip ..." + read -n1 -s fi fi } +bootstrapHeader() { + # $1 = ROOT/SWAP + local HEADERDEVS=$(devicelist) + eval local headerloc='"${CRYPT_'${1}'_HEADER}"' + + findmediamount "header" "${headerloc}" "CRYPT_${1}_HEADERDEV" "/mnt/header" ${HEADERDEVS} +} + bootstrapKey() { # $1 = ROOT/SWAP local KEYDEVS=$(devicelist) @@ -294,23 +406,25 @@ cache_cd_contents() { # echo ${z} good_msg "Copying loop file for caching ..." # Verify that the needed directory exists - mkdir -p "$(dirname ${NEW_ROOT}/mnt/${LOOP})" - cp -a ${CDROOT_PATH}/${LOOP} ${NEW_ROOT}/mnt/${LOOP} + run mkdir -p "$(dirname ${NEW_ROOT}/mnt/${LOOP})" + run cp -a ${CDROOT_PATH}/${LOOP} ${NEW_ROOT}/mnt/${LOOP} if [ $? -ne 0 ] then - warn_msg "Failed to cache the loop file! Lack of RAM?" - rm -rf ${NEW_ROOT}/mnt/${LOOP} 2>/dev/null - rm -rf ${NEW_ROOT}/mnt/livecd.* 2>/dev/null - rm -rf ${NEW_ROOT}/mnt/image.* 2>/dev/null - rm -rf ${NEW_ROOT}/mnt/zisofs 2>/dev/null + warn_msg "Failed to cache the loop file! Lack of RAM?" 0 + run rm -rf ${NEW_ROOT}/mnt/${LOOP} 2>/dev/null + run rm -rf ${NEW_ROOT}/mnt/livecd.* 2>/dev/null + run rm -rf ${NEW_ROOT}/mnt/image.* 2>/dev/null + run rm -rf ${NEW_ROOT}/mnt/zisofs 2>/dev/null fi fi fi } mount_sysfs() { - mount -t sysfs sysfs /sys -o noexec,nosuid,nodev >/dev/null 2>&1 - [ $? -eq 0 ] || bad_msg "Failed to mount /sys!" + if ! run mount -t sysfs sysfs /sys -o noexec,nosuid,nodev >/dev/null 2>&1 + then + bad_msg "Failed to mount /sys!" + fi } # Check support for both aufs and overlayfs @@ -345,8 +459,8 @@ is_union_modules() { else warn_msg "Adding all modules in ${mod_dir}" - mkdir /mnt/modules - mount "${mod_dir}" /mnt/modules + run mkdir /mnt/modules + run mount "${mod_dir}" /mnt/modules union_insert_modules /mnt/modules fi fi @@ -361,12 +475,18 @@ is_union_modules() { # aufs_insert_dir() { # Always mount it over the precedent (add:1:) - if mount -n -o "remount,add:1:$2=rr" aufs "$1" + if run mount -n -o "remount,add:1:$2=rr" aufs "$1" then good_msg "Addition of $2 to $1 successful" fi } +udevsettle() { + local timeout=${1-${GK_UDEV_TIMEOUT}} + + run udevadm settle --timeout=${timeout} +} + # Insert all modules found in $1, usually $CDROOT_PATH # added to allow users to add their own apps. union_insert_modules() { @@ -409,18 +529,18 @@ union_mod() { then if [ ! -d "${aufs_union}"/mnt/"${mod}" ] then - mkdir -p "${aufs_union}"/mnt/modules/"${mod}" || return + run mkdir -p "${aufs_union}"/mnt/modules/"${mod}" || return fi - mount -o loop,ro "$2" "${aufs_union}"/mnt/modules/"${mod}" + run mount -o loop,ro "$2" "${aufs_union}"/mnt/modules/"${mod}" aufs_insert_dir "${aufs_union}" "${aufs_union}"/mnt/modules/"${mod}" else if [ ! -d "${mod_dir}/.${mod}" ] then - mkdir -p "${mod_dir}/.${mod}" || return + run mkdir -p "${mod_dir}/.${mod}" || return fi - mount -o loop,ro "$2" "${mod_dir}/.${mod}" + run mount -o loop,ro "$2" "${mod_dir}/.${mod}" fi } @@ -450,7 +570,7 @@ conf_rc_no_umounts() { if [ -n "${cmd}" ] then - sed -i "${cmd%;}" ${conf} + run sed -i "${cmd%;}" ${conf} test_success "Unable to edit /etc/conf.d/localmount" fi fi @@ -462,6 +582,17 @@ conf_rc_no_umounts() { fi } +is_debug() { + local is_debug=1 + + if [ -f "${GK_DEBUGMODE_STATEFILE}" ] + then + is_debug=0 + fi + + return ${is_debug} +} + # is_int "${A}" ["${B}"..] # NOTE we consider a leading 0 false as it would be interpreted as octal is_int() { @@ -475,6 +606,55 @@ is_int() { done } +is_log_enabled() { + if [ -z "${GK_INIT_LOG}" ] + then + return 1 + elif [ -f "${GK_INIT_LOG_DISABLED}" ] + then + return 1 + fi + + return 0 +} + +is_quiet() { + is_true "${QUIET}" && return 0 + + return 1 +} + +is_true() { + case "${1}" in + 1) + return 0 + ;; + [Tt][Rr][Uu][Ee]) + return 0 + ;; + [Tt]) + return 0 + ;; + [Yy][Ee][Ss]) + return 0 + ;; + [Yy]) + return 0 + ;; + esac + + return 1 +} + +is_userinteraction_allowed() { + if [ -f "${GK_USERINTERACTION_DISABLED_STATEFILE}" ] + then + return 1 + fi + + return 0 +} + # Function to create an ext2 fs on $aufs_dev, $aufs_dev_mnt mountpoint create_changefs() { local size @@ -493,13 +673,13 @@ create_changefs() { then bad_msg "Please give a size of at least 16 Megabytes" else - if dd if=/dev/zero "of=${aufs_dev_mnt}${aufs_union_file}" bs=1 seek="${size}"M count=0 >/dev/null 2>&1 + if run dd if=/dev/zero "of=${aufs_dev_mnt}${aufs_union_file}" bs=1 seek="${size}"M count=0 >/dev/null 2>&1 then good_msg "Creation of ${aufs_union_file}, ${size}MB on ${aufs_dev successful}, formatting it ext2" - mke2fs -F "${aufs_dev_mnt}${aufs_union_file}" >/dev/null 2>&1 + run mke2fs -F "${aufs_dev_mnt}${aufs_union_file}" >/dev/null 2>&1 break else - rm "${aufs_dev_mnt}${aufs_union_file}" + run rm "${aufs_dev_mnt}${aufs_union_file}" bad_msg "Unable to create ${aufs_union_file#*/} on ${aufs_dev} of ${size}MB" bad_msg "Ensure your disk is not full or read-only" @@ -507,7 +687,7 @@ create_changefs() { if [ "${doabort}" = 'a' ] then bad_msg "Aborting creation of ${aufs_union_file}!" - umount "${aufs_dev}" && rmdir "${aufs_dev_mnt}" + run umount "${aufs_dev}" && rmdir "${aufs_dev_mnt}" return 1 fi fi @@ -551,7 +731,9 @@ setup_aufs() { then good_msg "Mounting ${aufs_dev} to ${aufs_memory} for aufs support" - if ! mount -t auto "${aufs_dev}" "${aufs_dev_mnt}" >/dev/null 2>&1 + local mounttype=$(determine_fs "${aufs_dev}" "auto") + + if ! run mount -t ${mounttype} "${aufs_dev}" "${aufs_dev_mnt}" >/dev/null 2>&1 then bad_msg "Mount of ${aufs_dev} failed, falling back to ramdisk based aufs" unset aufs_dev @@ -561,12 +743,12 @@ setup_aufs() { # Check and attempt to create the AUFS union file if [ ! -e ${aufs_dev_mnt}${aufs_union_file} ] && [ -n "${aufs_dev}" ] then - create_changefs && mount -t auto "${aufs_dev_mnt}${aufs_union_file}" "${aufs_memory}" + create_changefs && run mount -t auto "${aufs_dev_mnt}${aufs_union_file}" "${aufs_memory}" elif [ -n "${aufs_dev}" ] then while :; do - if mount -t auto "${aufs_dev_mnt}${aufs_union_file}" "${aufs_memory}" >/dev/null 2>&1 + if run mount -t auto "${aufs_dev_mnt}${aufs_union_file}" "${aufs_memory}" >/dev/null 2>&1 then if [ "${aufs_union_file}" = "/casper-rw" ] then @@ -578,21 +760,21 @@ setup_aufs() { if ! hash e2fsck >/dev/null 2>&1 then - bad_msg "/sbin/e2fsck not found! aborting filesystem check" + bad_msg "e2fsck not found! aborting filesystem check" bad_msg "Moving ${aufs_union_file#*/} to ${aufs_union_file#*/}.bad" - mv "${aufs_dev_mnt}${aufs_union_file}" "${aufs_dev_mnt}${aufs_union_file}.bad" + run mv "${aufs_dev_mnt}${aufs_union_file}" "${aufs_dev_mnt}${aufs_union_file}.bad" break fi - if e2fsck "${aufs_dev_mnt}${aufs_union_file}" >/dev/null 2>&1 + if run e2fsck "${aufs_dev_mnt}${aufs_union_file}" >/dev/null 2>&1 then good_msg "e2fsck ran successfully. Please verify data after bootup" else bad_msg "Your ${aufs_union_file#*/} image might be corrupted" bad_msg "moving ${aufs_union_file#*/} to ${aufs_union_file#*/}.bad" - mv "${aufs_dev_mnt}${aufs_union_file}" "${aufs_dev_mnt}${aufs_union_file}.bad" + run mv "${aufs_dev_mnt}${aufs_union_file}" "${aufs_dev_mnt}${aufs_union_file}.bad" break fi fi @@ -617,22 +799,22 @@ setup_aufs() { bad_msg "Falling back to ramdisk based aufs" good_msg "Mounting ramdisk to ${aufs_memory} for aufs support" - mount -t tmpfs tmpfs "${aufs_memory}" + run mount -t tmpfs tmpfs "${aufs_memory}" else aufs_xino=${aufs_memory}/xino - mkdir -p "${aufs_xino}" - mount -t tmpfs aufs-xino "${aufs_xino}" + run mkdir -p "${aufs_xino}" + run mount -t tmpfs aufs-xino "${aufs_xino}" fi else aufs_xino=${aufs_memory} good_msg "Mounting ramdisk to ${aufs_memory} for aufs support" - mount -t tmpfs tmpfs "${aufs_memory}" + run mount -t tmpfs tmpfs "${aufs_memory}" fi - mkdir -p "${aufs_branch}" - if ! mount -t aufs -n -o "nowarn_perm,udba=none,xino=${aufs_xino}/.aufs.xino,br:${aufs_branch}=rw" aufs "${aufs_union}" + run mkdir -p "${aufs_branch}" + if ! run mount -t aufs -n -o "nowarn_perm,udba=none,xino=${aufs_xino}/.aufs.xino,br:${aufs_branch}=rw" aufs "${aufs_union}" then bad_msg "Can't setup union ${aufs_union} in directory!" aufs=0 @@ -646,14 +828,14 @@ setup_overlayfs() { local workdir="${overlay}/.work" local static=/mnt/livecd - rundebugshell overlayfs + run_debug_shell overlayfs for i in "${overlay}" "${static}" do - [ ! -d "${i}" ] && mkdir -p "${i}" + [ ! -d "${i}" ] && run mkdir -p "${i}" done good_msg "Loading overlayfs" - modprobe overlay >/dev/null 2>&1 + run modprobe overlay >/dev/null 2>&1 checkfs overlay mount -t squashfs -o loop,ro "${CDROOT_PATH}/${LOOPEXT}${LOOP}" "${static}" @@ -661,7 +843,7 @@ setup_overlayfs() { mkdir "${upperdir}" "${workdir}" is_union_modules overlayfs - mount -t overlay overlay -o lowerdir="${static}${mod_path}",upperdir="${upperdir}",workdir="${workdir}" "${NEW_ROOT}" + run mount -t overlay overlay -o lowerdir="${static}${mod_path}",upperdir="${upperdir}",workdir="${workdir}" "${NEW_ROOT}" [ ! -d "${NEW_ROOT}${overlay}" ] && mkdir -p "${NEW_ROOT}${overlay}" [ ! -d "${NEW_ROOT}${static}" ] && mkdir -p "${NEW_ROOT}${static}" @@ -670,7 +852,14 @@ setup_overlayfs() { for i in "${overlay}" "${static}" do - mount --bind "${i}" "${NEW_ROOT}${i}" + run mount --bind "${i}" "${NEW_ROOT}${i}" + done + + # Setup the filesystem nodes and directories + # Copied from non-overlayfs logic earlier in this script + for i in ${CDROOT_PATH} /mnt/header /mnt/livecd /mnt/key /mnt/gentoo /tmp /tmp/.initrd /dev /proc /run /sys; do + run mkdir -p "${NEW_ROOT}${i}" + run chmod 755 "${NEW_ROOT}${i}" done # Did we populate the overlayfs modules path locations variable? @@ -678,7 +867,7 @@ setup_overlayfs() { then for i in ${mods} do - mount --bind "${overlay}/.${i}" "${NEW_ROOT}/${overlay}/.${i}" + run mount --bind "${overlay}/.${i}" "${NEW_ROOT}/${overlay}/.${i}" done fi } @@ -736,7 +925,7 @@ findnfsmount() { if [ "${CDROOT}" != '0' ] then good_msg "Attempting to mount NFS CD image on ${NFSROOT} with options ${NFSOPTIONS}" - mount -t nfs -o ${NFSOPTIONS} ${NFSROOT} ${CDROOT_PATH} + run mount -t nfs -o ${NFSOPTIONS} ${NFSROOT} ${CDROOT_PATH} if [ $? -eq 0 ] then REAL_ROOT="/dev/nfs" @@ -746,7 +935,7 @@ findnfsmount() { fi else good_msg "Attempting to mount NFS root on ${NFSROOT} with options ${NFSOPTIONS}" - mount -t nfs -o ${NFSOPTIONS} ${NFSROOT} ${NEW_ROOT} + run mount -t nfs -o ${NFSOPTIONS} ${NFSROOT} ${NEW_ROOT} if [ $? -eq 0 ] then REAL_ROOT="/dev/nfs" @@ -765,31 +954,39 @@ findnfsmount() { } find_real_device() { - local DEVICE="${1}" - case "${DEVICE}" in - UUID\=*|LABEL\=*|PARTUUID\=*) - local REAL_DEVICE="" + local device="${1}" + local real_device= + local candidate= + case "${device}" in + UUID\=*|LABEL\=*|PARTLABEL=*|PARTUUID\=*) local retval=1 if [ ${retval} -ne 0 ] then - REAL_DEVICE=$(findfs "${DEVICE}" 2>/dev/null) + candidate=$(findfs "${device}" 2>/dev/null) retval=$? fi if [ ${retval} -ne 0 ] then - REAL_DEVICE=$(blkid -o device -l -t "${DEVICE}" 2>/dev/null) + candidate=$(blkid -o device -l -t "${device}" 2>/dev/null) retval=$? fi - if [ ${retval} -eq 0 ] && [ -n "${REAL_DEVICE}" ] + if [ ${retval} -eq 0 ] && [ -n "${candidate}" ] then - DEVICE="${REAL_DEVICE}" + real_device="${candidate}" fi ;; + *) + candidate=$(readlink -f "${device}") + if [ -b "${candidate}" ] + then + real_device="${candidate}" + fi esac - printf "%s" "${DEVICE}" + + printf "%s" "${real_device}" } check_loop() { @@ -800,36 +997,104 @@ check_loop() { bad_msg 'Please export LOOP with a valid location, or reboot and pass a proper loop=...' bad_msg 'kernel command line!' - run_shell + run_emergency_shell fi } +run() { + local retval + + if "$@"; then + retval=$? + log_msg "Executed: '$*'" + else + retval=$? + log_msg "Failed (${retval}): '$*'" + fi + + return ${retval} +} + +run_debug_shell() { + is_debug || return + + good_msg 'Starting debug shell as requested by "debug" option.' 0 + good_msg "Run '${BOLD}gksosreport${NORMAL}' to generate debug report" 0 + good_msg "in case you want to file a bug report." 0 + good_msg "Stopping by: ${1}" 0 + run_shell +} + +run_emergency_shell() { + if is_userinteraction_allowed + then + echo + gksosreport + good_msg 'You might want to save "/run/initramfs/gksosreport.txt" to a USB stick or /boot' 0 + good_msg 'after mounting them and attach it to a bug report.' 0 + fi + + run_shell +} + run_shell() { + splash 'verbose' >/dev/null & + + if ! is_userinteraction_allowed + then + bad_msg "gk.userinteraction.disabled is set; Spawning a shell is disabled!" + return + fi + [ -x /bin/sh ] && SH=/bin/sh || SH=/bin/ash + run touch "${GK_SHELL_LOCKFILE}" + export PS1='rescueshell \w \# ' - if [ -n "${CONSOLE}" ] && [ -c "/dev/${CONSOLE}" ] + echo + GOOD=${BLUE} good_msg "${NORMAL}Welcome to ${BOLD}${GK_META_VERSION}${NORMAL} (${GK_META_BUILD_DATE}) ${BOLD}rescue shell${NORMAL}!" 0 + GOOD=${BLUE} good_msg "${NORMAL}...running Linux kernel ${BOLD}${KV}${NORMAL}" 0 + echo + + # Avoid /dev/{console,tty0} due to "can't access tty; job control turned off" problem; + # cttyhack will handle this for us... + if [ -n "${CONSOLE}" ] \ + && [ "${CONSOLE}" != "/dev/console" ] \ + && [ "${CONSOLE}" != "/dev/tty0" ] \ + && [ -c "${CONSOLE}" ] then - setsid ${SH} -c "exec sh --login </dev/${CONSOLE} >/dev/${CONSOLE} 2>&1" + log_msg "Opening rescue shell on ${CONSOLE} ..." + setsid ${SH} -c "exec sh --login 0<>${CONSOLE} 1<>${CONSOLE} 2<>${CONSOLE}" elif command -v cttyhack 1>/dev/null 2>&1 then + log_msg "Opening rescue shell using cttyhack ..." setsid cttyhack ${SH} --login elif [ -c '/dev/tty1' ] then - setsid ${SH} -c 'exec sh --login </dev/tty1 >/dev/tty1 2>&1' + log_msg "Opening rescue shell on /dev/tty1 fallback ..." + setsid ${SH} -c "exec sh --login 0<>/dev/tty1 1<>/dev/tty1 2<>/dev/tty1" else + log_msg "Opening rescue shell (last resort) ..." ${SH} --login fi + # Leave function early when /dev/null does not exist anymore, + # i.e. after failed switch_root call + [ ! -e /dev/null ] && return + + rm "${GK_SHELL_LOCKFILE}" + echo - :> "${GK_SHELL_LOCKFILE}" + # We maybe have called exec and dettached from main script; We + # must restore control... + exec 0<>${CONSOLE} 1<>${CONSOLE} 2<>${CONSOLE} } fs_type_in_use() { fs_type=$1 - cut -d ' ' -f 3 < /proc/mounts | fgrep -q "${fs_type}" + cut -d ' ' -f 3 < /proc/mounts | grep -Fq "${fs_type}" } mount_devfs() { @@ -845,16 +1110,20 @@ mount_devfs() { # Options copied from /etc/init.d/udev-mount, should probably be kept in sync if ! fs_type_in_use devtmpfs then - mount -t ${devfs} -o "exec,nosuid,mode=0755,size=10M" udev /dev \ + run mount -t ${devfs} -o "exec,nosuid,mode=0755,size=10M" udev /dev \ || bad_msg "Failed to mount /dev as ${devfs}" fi # http://git.busybox.net/busybox/plain/docs/mdev.txt if ! fs_type_in_use devpts then - mkdir -m 0755 /dev/pts - mount -t devpts -o gid=5,mode=0620 devpts /dev/pts || bad_msg "Failed to mount /dev/pts" + run mkdir -m 0755 /dev/pts + run mount -t devpts -o gid=5,mode=0620 devpts /dev/pts || bad_msg "Failed to mount /dev/pts" fi + + run mkdir -m 1777 /dev/shm + run mount -t tmpfs -o mode=1777,nosuid,nodev,strictatime tmpfs /dev/shm \ + || bad_msg "Failed to mount /dev/shm" } test_success() { @@ -863,56 +1132,128 @@ test_success() { if [ "${retcode}" != '0' ] then error_string=${1} - error_string="${error_string:-run command}" - bad_msg 'Failed to ${1}; failing back to the shell ...' - run_shell + error_string="${error_string:-Failed to run command}" + bad_msg "${error_string}; Failing back to the shell ..." + run_emergency_shell fi } +trim() { + local var="$*" + + var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters + var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters + + printf "%s" "${var}" +} + +log_msg() { + is_log_enabled || return + + if [ ! -f "${GK_INIT_LOG}" ] + then + touch "${GK_INIT_LOG}" 2>/dev/null || return + fi + + local log_prefix= + [ -n "${GK_INIT_LOG_PREFIX}" ] && log_prefix="${GK_INIT_LOG_PREFIX}: " + + local msg=${1} + + # Cannot use substitution because $msg could contain infinite color + # codes and substitution can't be greedy. + # Because Busybox's sed cannot deal with control characters, we + # have to get rid of all non-printable characters like "^[" first... + LANG=C echo "] ${log_prefix}${msg}" | sed \ + -e "s,[^[:print:]],,g" \ + -e 's,\(\\033\)\?\[[0-9;]\+m,,g' \ + | ts '[ %Y-%m-%d %H:%M:%.S' >> "${GK_INIT_LOG}" +} # msg functions arguments # $1 string # $2 hide flag good_msg() { - [ -n "${QUIET}" ] && [ -z "${DEBUG}" ] && return 0 - - msg_string=${1} + local msg_string=${1} msg_string="${msg_string:-...}" - [ "$2" != '1' ] && printf "%b\n" "${GOOD}>>${NORMAL}${BOLD} ${msg_string} ${NORMAL}" + + log_msg "[OK] ${msg_string}" + + is_true "${2-${QUIET}}" || printf "%b\n" "${GOOD}>>${NORMAL}${BOLD} ${msg_string} ${NORMAL}" } good_msg_n() { - [ -n "${QUIET}" ] && [ -z "${DEBUG}" ] && return 0 - - msg_string=${1} + local msg_string=${1} msg_string="${msg_string:-...}" - [ "$2" != '1' ] && printf "%b" "${GOOD}>>${NORMAL}${BOLD} ${msg_string}" + + log_msg "[OK] ${msg_string}" + + is_true "${2-${QUIET}}" || printf "%b" "${GOOD}>>${NORMAL}${BOLD} ${msg_string}" } bad_msg() { - msg_string=${1} + local msg_string=${1} msg_string="${msg_string:-...}" - if [ "$2" != '1' ] - then - splash 'verbose' >/dev/null & - printf "%b\n" "${BAD}!!${NORMAL}${BOLD} ${msg_string} ${NORMAL}" - fi + + log_msg "[!!] ${msg_string}" + + splash 'verbose' >/dev/null & + printf "%b\n" "${BAD}!!${NORMAL}${BOLD} ${msg_string} ${NORMAL}" } warn_msg() { - msg_string=${1} + local msg_string=${1} + msg_string="${msg_string:-...}" + + log_msg "[**] ${msg_string}" + + is_true "${2-${QUIET}}" || printf "%b\n" "${WARN}**${NORMAL}${BOLD} ${msg_string} ${NORMAL}" +} + +warn_msg_n() { + local msg_string=${1} msg_string="${msg_string:-...}" - [ "$2" != '1' ] && printf "%b\n" "${WARN}**${NORMAL}${BOLD} ${msg_string} ${NORMAL}" + + log_msg "[**] ${msg_string}" + + is_true "${2-${QUIET}}" || printf "%b" "${WARN}**${NORMAL}${BOLD} ${msg_string}" +} + +write_env_file() { + local env_file=${1} + shift + + run touch "${env_file}" + + local varname= varvalue= + for varname in $* + do + eval varvalue=\$${varname} + echo "${varname}='${varvalue}'" >> "${env_file}" + done } crypt_filter() { + local ask_pass=${2} + good_msg "Using the following decryption command: ${1}" ${CRYPT_SILENT} + if [ "${CRYPT_SILENT}" = '1' ] then - eval $1 >/dev/null 2>&1 + eval run ${1} + elif [ "${PLYMOUTH}" = '1' ] && [ ${ask_pass} -eq 1 ] + then + local ply_cmd_file="$(mktemp -t 'ply_cmd.XXXXXX' 2>/dev/null)" + printf '#!/bin/sh\n%s\n' "${1}" > "${ply_cmd_file}" + run chmod 500 "${ply_cmd_file}" + plymouthRun ask-for-password --prompt "Enter LUKS passphrase" \ + --number-of-tries=3 --command="${ply_cmd_file}" + res=$? + run rm "${ply_cmd_file}" >/dev/null 2>&1 + return ${res} else splash 'verbose' >/dev/null & - eval $1 + eval run ${1} res=$? if [ ${res} -eq 0 ] then @@ -922,6 +1263,55 @@ crypt_filter() { fi } +process_initramfs_mounts() { + local fslist= + + if [ -f "${NEW_ROOT}/etc/initramfs.mounts" ] + then + fslist="$(get_mounts_list)" + else + fslist="/usr" + fi + + local dev= fs= fstype= opts= mnt= cmd= + for fs in ${fslist} + do + mnt="${NEW_ROOT}${fs}" + if run mountpoint -q "${mnt}" + then + good_msg "${fs} already mounted, skipping..." + continue + fi + + dev=$(get_mount_device "${fs}") + [ -z "${dev}" ] && continue + # Resolve it like util-linux mount does + [ -L "${dev}" ] && dev=$(realpath "${dev}") + # In this case, it's probably part of the filesystem + # and not a mountpoint + [ -z "${dev}" ] && continue + + fstype=$(get_mount_fstype "${fs}") + if get_mount_options "${fs}" | grep -Fq bind + then + opts="bind" + dev="${NEW_ROOT}${dev}" + else + # ro must be trailing, and the options will always + # contain at least 'defaults' + opts="$(get_mount_options ${fs} | strip_mount_options)" + opts="${opts},ro" + fi + + cmd="mount -t ${fstype} -o ${opts} ${dev} ${mnt}" + good_msg "Mounting ${dev} as ${fs}: ${cmd}" + if ! run ${cmd} + then + bad_msg "Unable to mount ${dev} for ${fs}" + fi + done +} + prompt_user() { # $1 = variable whose value is the path (examples: "REAL_ROOT", # "LUKS_KEYDEV") @@ -938,33 +1328,91 @@ prompt_user() { fi [ -n "${3}" ] && local explnt=" or : ${3}" || local explnt="." + splash 'verbose' >/dev/null & bad_msg "Could not find the ${2} in ${oldvalue}${explnt}" + + if [ -f "${GK_USERINTERACTION_DISABLED_STATEFILE}" ] + then + bad_msg "gk.userinteraction.disabled is set; No user interaction allowed!" + + wait_sshd + + if [ -f "${GK_SSHD_LOCKFILE}" ] + then + warn_msg "The lockfile at '${GK_SSHD_LOCKFILE}' exists." 0 + warn_msg "The boot process will be paused until the lock is removed." 0 + while true + do + if [ -f "${GK_SSHD_LOCKFILE}" ] + then + sleep 1 + else + break + fi + done + fi + + local timeout=${GK_PROMPT_TIMEOUT} + [ ${timeout} -eq 0 ] && timeout=10 + + warn_msg_n "System will automatically reboot in ${timeout} seconds ..." 0 + while [ ${timeout} -gt 0 ] + do + let timeout=${timeout}-1 + sleep 1 + printf "." + done + echo + + reboot -f + fi + bad_msg "Please specify another value or:" bad_msg "- press Enter for the same" bad_msg '- type "shell" for a shell' bad_msg '- type "q" to skip ...' printf "%s" "${2}(${oldvalue}) :: " - read ${1} - #if [ $? -gt 0 ] - #then - # # prompt timed out - # printf "\n" - #fi + + if [ "${GK_PROMPT_TIMEOUT}" = '0' ] + then + read ${1} + else + local read_timeout_timestamp + let read_timeout_timestamp=$(date +%s)+${GK_PROMPT_TIMEOUT} + + echo "# Could not find the ${2} in ${oldvalue}${explnt}" > "${GK_PROMPT_FILE}" + echo "# Please specify another value (file will be processed at $(date -d @${read_timeout_timestamp}):" >> "${GK_PROMPT_FILE}" + echo "${1}=${oldvalue}" >> "${GK_PROMPT_FILE}" + read -t ${GK_PROMPT_TIMEOUT} ${1} + if [ $? -gt 0 ] + then + # prompt timed out + printf "\n" + + if [ -f "${GK_PROMPT_FILE}" ] + then + warn_msg "Timeout! Trying to read answer from '${GK_PROMPT_FILE}' ..." 0 + . "${GK_PROMPT_FILE}" && run rm "${GK_PROMPT_FILE}" + fi + fi + fi case $(eval echo '$'${1}) in 'q') eval ${1}'='${oldvalue} - warn_msg "Skipping step, this will likely cause a boot failure." + warn_msg "Skipping step, this will likely cause a boot failure." 0 ;; 'shell') eval ${1}'='${oldvalue} - warn_msg "To leave and try again just press <Ctrl>+D" - run_shell + warn_msg "To leave and try again just press <Ctrl>+D" 0 + run_emergency_shell ;; '') eval ${1}'='${oldvalue} ;; esac + + splash 'quiet' >/dev/null & } cmdline_hwopts() { @@ -1011,35 +1459,25 @@ cmdline_hwopts() { MY_HWOPTS=${TMP_HWOPTS} } -load_modules() { - # Load modules listed in MY_HWOPTS if /lib/modules exists for the running - # kernel version - if [ -d "/lib/modules/${KV}" ] - then - good_msg 'Loading modules' - # Load appropriate kernel modules - for modules in ${MY_HWOPTS} - do - modules_scan ${modules} - done - else - good_msg 'Skipping module load; no modules in the ramdisk!' - fi -} - setup_keymap() { if [ "${DO_keymap}" ] then + local console=$(get_active_console) + if echo "${console}" | grep -qF 'ttyS' + then + warn_msg "Active console is ${console}; Skipping dokeymap ..." + return + fi + if [ ! -e /dev/vc/0 -a ! -e /dev/tty0 ] then DEVBIND=1 - mount -o bind ${NEW_ROOT}/dev /dev + run mount -o bind ${NEW_ROOT}/dev /dev fi - [ ! -e /dev/tty0 ] && ln -s /dev/tty1 /dev/tty0 [ -f /lib/keymaps/keymapList ] && chooseKeymap - [ "${DEVBIND}" = '1' ] && umount /dev + [ "${DEVBIND}" = '1' ] && run umount /dev fi } @@ -1055,66 +1493,62 @@ chooseKeymap() { if [ -z "${keymap}" ] then splash 'verbose' >/dev/null & - cat /lib/keymaps/keymapList + run cat /lib/keymaps/keymapList read -t 10 -p '<< Load keymap (Enter for default): ' keymap case ${keymap} in 1|azerty) keymap=azerty ;; 2|be) keymap=be ;; - 3|bg) keymap=bg ;; - 4|br-a) keymap=br-a ;; - 5|br-l) keymap=br-l ;; - 6|by) keymap=by ;; - 7|cf) keymap=cf ;; - 8|croat) keymap=croat ;; - 9|cz) keymap=cz ;; - 10|de) keymap=de ;; - 11|dk) keymap=dk ;; - 12|dvorak) keymap=dvorak ;; - 13|es) keymap=es ;; - 14|et) keymap=et ;; - 15|fi) keymap=fi ;; - 16|fr) keymap=fr ;; - 17|gr) keymap=gr ;; - 18|hu) keymap=hu ;; - 19|il) keymap=il ;; - 20|is) keymap=is ;; - 21|it) keymap=it ;; - 22|jp) keymap=jp ;; - 23|la) keymap=la ;; - 24|lt) keymap=lt ;; - 25|mk) keymap=mk ;; - 26|nl) keymap=nl ;; - 27|no) keymap=no ;; - 28|pl) keymap=pl ;; - 29|pt) keymap=pt ;; - 30|ro) keymap=ro ;; - 31|ru) keymap=ru ;; - 32|se) keymap=se ;; - 33|sg) keymap=sg ;; - 34|sk-y) keymap=sk-y ;; - 35|sk-z) keymap=sk-z ;; - 36|slovene) keymap=slovene ;; - 37|trf) keymap=trf ;; - 38|trq) keymap=trq ;; - 39|ua) keymap=ua ;; - 40|uk) keymap=uk ;; - 41|us) keymap=us ;; - 42|wangbe) keymap=wangbe ;; - 43|sf|ch*) keymap=sf ;; + 3|bepo) keymap=bepo ;; + 4|bg) keymap=bg ;; + 5|br-a) keymap=br-a ;; + 6|br-l) keymap=br-l ;; + 7|by) keymap=by ;; + 8|cf) keymap=cf ;; + 9|colemak) keymap=colemak ;; + 10|croat) keymap=croat ;; + 11|cz) keymap=cz ;; + 12|de) keymap=de ;; + 13|dk) keymap=dk ;; + 14|dvorak) keymap=dvorak ;; + 15|es) keymap=es ;; + 16|et) keymap=et ;; + 17|fi) keymap=fi ;; + 18|fr) keymap=fr ;; + 19|gr) keymap=gr ;; + 20|hu) keymap=hu ;; + 21|il) keymap=il ;; + 22|is) keymap=is ;; + 23|it) keymap=it ;; + 24|jp) keymap=jp ;; + 25|la) keymap=la ;; + 26|lt) keymap=lt ;; + 27|mk) keymap=mk ;; + 28|nl) keymap=nl ;; + 29|no) keymap=no ;; + 30|pl) keymap=pl ;; + 31|pt) keymap=pt ;; + 32|ro) keymap=ro ;; + 33|ru) keymap=ru ;; + 34|se) keymap=se ;; + 35|sf|ch*) keymap=sf ;; + 36|sg) keymap=sg ;; + 37|sk-y) keymap=sk-y ;; + 38|sk-z) keymap=sk-z ;; + 39|slovene) keymap=slovene ;; + 40|trf) keymap=trf ;; + 41|ua) keymap=ua ;; + 42|uk) keymap=uk ;; + 43|us) keymap=us ;; + 44|wangbe) keymap=wangbe ;; esac fi + if [ -e /lib/keymaps/${keymap}.map ] then good_msg "Loading the '${keymap}' keymap ..." - loadkmap < /lib/keymaps/${keymap}.map -# xkeymap=${keymap} -# echo ${keymap} | egrep -e "[0-9]+" >/dev/null 2>&1 -# if [ $? -eq 0 ] -# then -# xkeymap=$(tail -n 8 /lib/keymaps/keymapList | grep ${keymap} | sed -r "s/.*\s+${keymap}\s+([a-z-]+).*/\1/g" | egrep -v 1) -# fi - mkdir -p /etc/sysconfig -# echo "XKEYBOARD=${xkeymap}" > /etc/sysconfig/keyboard + run loadkmap < /lib/keymaps/${keymap}.map + + run mkdir -p /etc/sysconfig echo "XKEYBOARD=${keymap}" > /etc/sysconfig/keyboard splash set_msg "Set keymap to '${keymap}'" elif [ -z "${keymap}" ] @@ -1135,20 +1569,70 @@ chooseKeymap() { copyKeymap() { if [ -e /etc/sysconfig/keyboard -a ${CDROOT} -eq 1 ] then - [ ! -d ${NEW_ROOT}/etc/sysconfig ] && mkdir -p ${NEW_ROOT}/etc/sysconfig - cp /etc/sysconfig/keyboard ${NEW_ROOT}/etc/sysconfig/keyboard + [ ! -d ${NEW_ROOT}/etc/sysconfig ] && run mkdir -p ${NEW_ROOT}/etc/sysconfig + run cp /etc/sysconfig/keyboard ${NEW_ROOT}/etc/sysconfig/keyboard fi } -# This helper function is to be called using call_func_timeout. -# It enables us to wait a reasonable amount of time until /dev/zfs appears. -waitForZFS() { - while [ ! -c /dev/zfs ] - do - echo >/dev/null - done +splash() { + if [ "${FBSPLASH}" = '1' ] + then + return 0 + elif [ "${PLYMOUTH}" = '1' ] + then + case "${1}" in + init) + plymouthInit + ;; - exit 1 + verbose) + plymouthRun --hide-splash + ;; + + set_msg) + plymouthRun --update="${2}" + ;; + + quiet) + plymouthRun --show-splash + ;; + + hasroot) + plymouthRun --newroot="${2}" + ;; + esac + fi +} + +plymouthRun() { + run plymouth --ping 2>/dev/null || return $? + run plymouth "${@}" 2>/dev/null +} + +plymouthInit() { + good_msg "Starting Plymouth..." + run mkdir -p -m 0755 /run/plymouth || return 1 + + # Make sure that udev is done loading tty and drm + run udevadm trigger --action=add --attr-match=class=0x030000 >/dev/null 2>&1 + run udevadm trigger --action=add --subsystem-match=graphics \ + --subsystem-match=drm --subsystem-match=tty >/dev/null 2>&1 + udevsettle + + run plymouthd --mode=boot --attach-to-session \ + --pid-file=/run/plymouth/pid + if [ $? -ne 0 ] + then + bad_msg "Can't start plymouthd!" + PLYMOUTH=0 + return 1 + fi + + plymouthRun --show-splash + if [ $? -eq 0 ] + then + good_msg "Plymouth initialized" + fi } start_volumes() { @@ -1156,196 +1640,183 @@ start_volumes() { # a symlink, which should hopefully fix bug #142775 and bug #147015 if [ -e /dev/device-mapper ] && [ ! -e /dev/mapper/control ] then - mkdir -p /dev/mapper - ln -sf /dev/device-mapper /dev/mapper/control - fi - - if [ "${USE_MDADM}" = '1' ] - then - if [ -x '/sbin/mdadm' ] - then - /sbin/mdadm --assemble --scan - #Intel Matrix RAID (and possibly others) have a container layer above the actual volumes, - #So we have to look for volumes that haven't been activated. - mdadm -IRs - else - bad_msg "mdadm not found: skipping mdadm raid assembly!" - fi + run mkdir -p /dev/mapper + run ln -sf /dev/device-mapper /dev/mapper/control fi if [ "${USE_MULTIPATH_NORMAL}" = '1' ] then - for multipath_path in /sbin/multipath /bin/multipath MISSING - do - [ -x "${multipath_path}" ] && break - done - - for dmsetup_path in /sbin/dmsetup /bin/dmsetup MISSING - do - [ -x "${dmsetup_path}" ] && break - done - - for kpartx_path in /sbin/kpartx /bin/kpartx MISSING - do - [ -x "${kpartx_path}" ] && break - done - - fail=0 - [ "${multipath_path}" = "MISSING" ] && fail=1 && bad_msg "domultipath called, but multipath binary missing! Skipping multipath" - [ "${dmsetup_path}" = "MISSING" ] && fail=1 && bad_msg "domultipath called, but dmsetup binary missing! Skipping multipath" - [ "${kpartx_path}" = "MISSING" ] && fail=1 && bad_msg "domultipath called, but kpartx binary missing! Skipping multipath" - - if [ ${fail} -eq 0 ] + if ! hash multipath >/dev/null 2>&1 then + bad_msg "domultipath called, but multipath binary missing! Skipping multipath" + else good_msg "Scanning for multipath devices" - good_msg ":: Populating scsi_id info for libudev queries" - mkdir -p /run/udev/data - local ech - for ech in /sys/block/* - do - local tgtfile=b$(cat ${ech}/dev) - /lib/udev/scsi_id -g -x /dev/${ech##*/} |sed -e 's/^/E:/' >/run/udev/data/${tgtfile} - done + local multipath_cmd="run multipath -v 0 2>&1" + is_log_enabled && multipath_cmd="${multipath_cmd} | tee -a '${GK_INIT_LOG}'" - ${multipath_path} -v 0 - sleep 2 - good_msg "Activating multipath devices" - ${dmsetup_path} ls --target multipath --exec "${kpartx_path} -a -v" + eval "${multipath_cmd}" + if [ $? -ne 0 ] + then + bad_msg "Scanning for multipath devices failed!" + else + udevsettle + fi fi fi if [ "${USE_DMRAID_NORMAL}" = '1' ] then - if [ -x '/sbin/dmraid' ] + if ! hash dmraid >/dev/null 2>&1 then - good_msg "Activating Device-Mapper RAID(s)" + bad_msg "dodmraid invoked but 'dmraid' not found; Skipping dmraid activation ..." + else + good_msg "Activating Device-Mapper RAID(s) ..." + local dmraid_cmd="run dmraid -ay" if [ -z "${DMRAID_OPTS}" ] then - /sbin/dmraid -ay + dmraid_cmd="${dmraid_cmd} 2>&1" else - /sbin/dmraid -ay ${DMRAID_OPTS} + dmraid_cmd="${dmraid_cmd} ${DMRAID_OPTS} 2>&1" + fi + is_log_enabled && dmraid_cmd="${dmraid_cmd} | tee -a '${GK_INIT_LOG}'" + + eval "${dmraid_cmd}" + if [ $? -ne 0 ] + then + bad_msg "Activation of Device-Mapper RAID(s) failed!" + else + udevsettle fi - [ -x '/sbin/kpartx' ] && /sbin/dmsetup ls --exec '/sbin/kpartx -a -s' fi fi if [ "${USE_LVM_NORMAL}" = '1' ] then - for lvm_path in /sbin/lvm /bin/lvm MISSING - do - [ -x "${lvm_path}" ] && break - done - - if [ "${lvm_path}" = "MISSING" ] + if ! hash lvm >/dev/null 2>&1 then - bad_msg "dolvm invoked, but LVM binary not available! skipping LVM volume group activation!" + bad_msg "dolvm invoked but LVM binary not available; Skipping LVM volume group activation ..." else - for dev in ${RAID_DEVICES} - do - setup_md_device "${dev}" - done - - # This is needed for LVM to accept the following logic - lvm_commands="#! ${lvm_path}" - # If there is a cache, update it. Unbreak at least dmcrypt - [ -d /etc/lvm/cache ] && lvm_commands="${lvm_commands} \nvgscan" - - # To activate volumegroups on all devices in the cache - lvm_commands="${lvm_commands} \nvgchange -ay --sysinit" - - # To create symlinks so users can use real_root=/dev/vg/root - # This needs to run after vgchange, using vgchange --mknodes is too - # early. - lvm_commands="${lvm_commands} \nvgmknodes --ignorelockingfailure" + if [ -d /etc/lvm/cache ] + then + good_msg "Scanning for volume groups ..." - # And finally execute it all (/proc/... needed if lvm is compiled without readline) - good_msg "Scanning for and activating Volume Groups" - printf "%b\n" "${lvm_commands}" | ${lvm_path} /proc/self/fd/0 - fi - fi + local lvm_cmd="run lvm vgscan 2>&1" + is_log_enabled && lvm_cmd="${lvm_cmd} | tee -a '${GK_INIT_LOG}'" + is_quiet && lvm_cmd="${lvm_cmd} 1>/dev/null" - if [ "${USE_BCACHE}" = '1' ] - then - if [ ! -e /sys/fs/bcache/register_quiet ] - then - warn_msg "'/sys/fs/bcache/register_quiet' does not exist. Missing kernel driver? Skipping dobcache ..." - else - local i= - for i in $(awk '$4 !~ /^(name$|$)/ { print $4 }' /proc/partitions) - do - if [ -e "/dev/${i}" ] + eval "${lvm_cmd}" + if [ $? -ne 0 ] then - # Push all the block devices to register_quiet - # If its bcache, it will bring it up, if not, it will simply ignore it. - echo "/dev/${i}" >/sys/fs/bcache/register_quiet 2>/dev/null + bad_msg "Scanning for volume groups failed!" else - warn_msg "'/dev/${i}' should exist but is missing; Ignoring ..." + udevsettle fi - done - fi - fi + fi - if [ "${USE_BTRFS}" = '1' ] - then - if [ -x '/sbin/btrfs' ] - then - /sbin/btrfs device scan - else - bad_msg "btrfs not found: skipping btrfs device scanning!" + good_msg "Activating volume groups ..." + + # To activate volumegroups on all devices in the cache + local lvm_cmd="run lvm vgchange -ay --sysinit 2>&1" + is_log_enabled && lvm_cmd="${lvm_cmd} | tee -a '${GK_INIT_LOG}'" + is_quiet && lvm_cmd="${lvm_cmd} 1>/dev/null" + + eval "${lvm_cmd}" + if [ $? -ne 0 ] + then + bad_msg "Activation of volume groups failed!" + fi fi fi if [ "${USE_ZFS}" = '1' ] then # Avoid race involving asynchronous module loading - if call_func_timeout waitForZFS 5 + if [ ! -e /dev/zfs ] then bad_msg "Cannot import ZFS pool because /dev/zfs is missing" elif [ -z "${ZFS_POOL}" ] then - good_msg "Importing ZFS pools" + good_msg "Importing ZFS pools ..." - /sbin/zpool import -N -a ${ZPOOL_CACHE} ${ZPOOL_FORCE} - if [ $? -eq 0 ] + local zfs_cmd="run /sbin/zpool import -N -a ${ZPOOL_CACHE} ${ZPOOL_FORCE} 2>&1" + is_log_enabled && zfs_cmd="${zfs_cmd} | tee -a '${GK_INIT_LOG}'" + is_quiet && zfs_cmd="${zfs_cmd} 1>/dev/null" + + eval "${zfs_cmd}" + if [ $? -ne 0 ] then - good_msg "Importing ZFS pools succeeded" - else - bad_msg "Imported ZFS pools failed" + bad_msg "Importing ZFS pools failed!" fi else - if [ "$(zpool list -H -o name ${ZFS_POOL} 2>&1)" = "${ZFS_POOL}" ] then good_msg "ZFS pool ${ZFS_POOL} already imported." if [ -n "${CRYPT_ROOT}" -o -n "${CRYPT_SWAP}" ] then - good_msg "LUKS detected. Reimporting ${ZFS_POOL}" - /sbin/zpool export -f "${ZFS_POOL}" - /sbin/zpool import -N ${ZPOOL_CACHE} ${ZPOOL_FORCE} "${ZFS_POOL}" + good_msg "LUKS detected. Reimporting ${ZFS_POOL} ..." + + local zfs_cmd="run /sbin/zpool export -f '${ZFS_POOL}' 2>&1" + is_log_enabled && zfs_cmd="${zfs_cmd} | tee -a '${GK_INIT_LOG}'" + is_quiet && zfs_cmd="${zfs_cmd} 1>/dev/null" + + eval "${zfs_cmd}" + if [ $? -ne 0 ] + then + bad_msg "Exporting ZFS pools failed!" + else + udevsettle + fi + + zfs_cmd="run /sbin/zpool import -N ${ZPOOL_CACHE} ${ZPOOL_FORCE} '${ZFS_POOL}' 2>&1" + is_log_enabled && zfs_cmd="${zfs_cmd} | tee -a '${GK_INIT_LOG}'" + is_quiet && zfs_cmd="${zfs_cmd} 1>/dev/null" + + eval "${zfs_cmd}" + if [ $? -ne 0 ] + then + bad_msg "Re-importing ZFS pools failed!" + fi fi else - good_msg "Importing ZFS pool ${ZFS_POOL}" + good_msg "Importing ZFS pool ${ZFS_POOL} ..." - /sbin/zpool import -N ${ZPOOL_CACHE} ${ZPOOL_FORCE} "${ZFS_POOL}" - if [ $? -eq 0 ] + local zfs_cmd="run /sbin/zpool import -N ${ZPOOL_CACHE} ${ZPOOL_FORCE} '${ZFS_POOL}' 2>&1" + is_log_enabled && zfs_cmd="${zfs_cmd} | tee -a '${GK_INIT_LOG}'" + is_quiet && zfs_cmd="${zfs_cmd} 1>/dev/null" + + eval "${zfs_cmd}" + if [ $? -ne 0 ] then - good_msg "Import of ${ZFS_POOL} succeeded" - else - bad_msg "Import of ${ZFS_POOL} failed" + bad_msg "Import of ${ZFS_POOL} failed!" fi fi fi fi + + udevsettle } start_iscsi() { + local iscsi_cmd + if [ ! -n "${ISCSI_NOIBFT}" ] then - good_msg "Activating iSCSI via iBFT" - iscsistart -b + good_msg "Activating iSCSI via iBFT ..." + + iscsi_cmd="run iscsistart -b 2>&1" + is_log_enabled && iscsi_cmd="${iscsi_cmd} | tee -a '${GK_INIT_LOG}'" + is_quiet && iscsi_cmd="${iscsi_cmd} 1>/dev/null" + + eval "${iscsi_cmd}" + if [ $? -ne 0 ] + then + bad_msg "Activation of iSCSI via iBFT failed!" + else + udevsettle + fi fi if [ -n "${ISCSI_INITIATORNAME}" ] && [ -n "${ISCSI_TARGET}" ] && [ -n "${ISCSI_ADDRESS}" ] @@ -1389,7 +1860,17 @@ start_iscsi() { ADDITIONAL="${ADDITIONAL} -d ${ISCSI_DEBUG}" fi - iscsistart -i "${ISCSI_INITIATORNAME}" -t "${ISCSI_TARGET}" -a "${ISCSI_ADDRESS}" ${ADDITIONAL} + iscsi_cmd="run iscsistart -i '${ISCSI_INITIATORNAME}' -t '${ISCSI_TARGET}' -a '${ISCSI_ADDRESS}' ${ADDITIONAL} 2>&1" + is_log_enabled && iscsi_cmd="${iscsi_cmd} | tee -a '${GK_INIT_LOG}'" + is_quiet && iscsi_cmd="${iscsi_cmd} 1>/dev/null" + + eval "${iscsi_cmd}" + if [ $? -ne 0 ] + then + bad_msg "Activation of iSCSI via cmdline failed!" + else + udevsettle + fi fi } @@ -1398,42 +1879,79 @@ start_iscsi() { # It is either the root or a swap, other devices are supported in the scripts provided with sys-fs/cryptsetup # $1 - root/swap openLUKS() { - if [ ! -x /sbin/cryptsetup ] + if ! hash cryptsetup >/dev/null 2>&1 then bad_msg "cryptsetup program is missing. Was initramfs built without --luks parameter?" exit 1 fi - case $1 in + case ${1} in root) local TYPE=ROOT ;; swap) local TYPE=SWAP ;; + *) + bad_msg "OpenLUKS(): Unknown type '${1}' specified!" + exit 1 + ;; esac - eval local LUKS_DEVICE='"${CRYPT_'${TYPE}'}"' LUKS_NAME="$1" LUKS_KEY='"${CRYPT_'${TYPE}'_KEY}"' - eval local LUKS_KEYDEV='"${CRYPT_'${TYPE}'_KEYDEV}"' LUKS_TRIM='"${CRYPT_'${TYPE}'_TRIM}"' + local LUKS_NAME="${1}" + eval local LUKS_DEVICE='"${CRYPT_'${TYPE}'}"' + eval local LUKS_HEADER='"${CRYPT_'${TYPE}'_HEADER}"' + eval local LUKS_HEADERDEV='"${CRYPT_'${TYPE}'_HEADERDEV}"' + eval local LUKS_HEADERDEV_FSTYPE='"${CRYPT_'${TYPE}'_HEADERDEV_FSTYPE}"' + eval local LUKS_KEY='"${CRYPT_'${TYPE}'_KEY}"' + eval local LUKS_KEYDEV='"${CRYPT_'${TYPE}'_KEYDEV}"' + eval local LUKS_KEYDEV_FSTYPE='"${CRYPT_'${TYPE}'_KEYDEV_FSTYPE}"' eval local OPENED_LOCKFILE='"${CRYPT_'${TYPE}'_OPENED_LOCKFILE}"' - local DEV_ERROR=0 KEY_ERROR=0 KEYDEV_ERROR=0 - local mntkey="/mnt/key/" crypt_filter_ret= cryptsetup_options='' + local ASK_PASS=0 + local DEV_ERROR=0 + local HEADER_ERROR=0 HEADERDEV_ERROR=0 + local KEY_ERROR=0 KEYDEV_ERROR=0 + local mntheader="/mnt/header/" mntkey="/mnt/key/" crypt_filter_ret= + + if [ -z "${LUKS_DEVICE}" ] + then + bad_msg "'crypt_${1}' kernel command-line argument is not set!" + exit 1 + fi while true do + # Reset cryptsetup_options on each iteration + eval local cryptsetup_options='"${CRYPT_'${TYPE}'_OPTIONS}"' + cryptsetup_options="$(trim "${cryptsetup_options}")" + local gpg_cmd="" if [ -e "${OPENED_LOCKFILE}" ] then good_msg "The LUKS device ${LUKS_DEVICE} meanwhile was opened by someone else." break # if crypt_silent=1 and some error occurs, enter shell quietly - elif [ \( ${CRYPT_SILENT} -eq 1 \) -a \( \( \( ${DEV_ERROR} -eq 1 \) -o \( ${KEY_ERROR} -eq 1 \) \) -o \( ${KEYDEV_ERROR} -eq 1 \) \) ] + elif [ \( ${CRYPT_SILENT} -eq 1 \) -a \( \( ${DEV_ERROR} -eq 1 \) \) ] + then + run_emergency_shell + elif [ \( ${CRYPT_SILENT} -eq 1 \) -a \( \( \( ${HEADER_ERROR} -eq 1 \) \) -o \( ${HEADERDEV_ERROR} -eq 1 \) \) ] + then + run_emergency_shell + elif [ \( ${CRYPT_SILENT} -eq 1 \) -a \( \( \( ${KEY_ERROR} -eq 1 \) \) -o \( ${KEYDEV_ERROR} -eq 1 \) \) ] then - run_shell + run_emergency_shell elif [ ${DEV_ERROR} -eq 1 ] then prompt_user "LUKS_DEVICE" "${LUKS_NAME}" DEV_ERROR=0 + elif [ ${HEADER_ERROR} -eq 1 ] + then + prompt_user "LUKS_HEADER" "${LUKS_NAME} header" + HEADER_ERROR=0 + elif [ ${HEADERDEV_ERROR} -eq 1 ] + then + prompt_user "LUKS_HEADERDEV" "${LUKS_NAME} header device" + HEADERDEV_ERROR=0 elif [ ${KEY_ERROR} -eq 1 ] then prompt_user "LUKS_KEY" "${LUKS_NAME} key" @@ -1446,133 +1964,292 @@ openLUKS() { LUKS_DEVICE=$(find_real_device "${LUKS_DEVICE}") if [ -z "${LUKS_DEVICE}" ] then - bad_msg "Looks like CRYPT_${TYPE} kernel cmdline argument is not set." ${CRYPT_SILENT} + bad_msg "Failed to find LUKS device. If crypt_${1} kernel command-line argument is correct you are probably missing kernel support for your storage!" ${CRYPT_SILENT} DEV_ERROR=1 continue fi - setup_md_device ${LUKS_DEVICE} - cryptsetup isLuks ${LUKS_DEVICE} - if [ $? -ne 0 ] + # Handle headers + if [ -n "${LUKS_HEADER}" ] then - bad_msg "The LUKS device ${LUKS_DEVICE} does not contain a LUKS header" ${CRYPT_SILENT} - DEV_ERROR=1 - continue - else - if [ "x${LUKS_TRIM}" = "xyes" ] + local REAL_LUKS_HEADERDEV="${LUKS_HEADERDEV}" + if [ ! -e "${mntheader}${LUKS_HEADER}" ] then - good_msg "Enabling TRIM support for ${LUKS_NAME} ..." ${CRYPT_SILENT} - cryptsetup_options="${cryptsetup_options} --allow-discards" - fi - - # Handle keys - if [ -n "${LUKS_KEY}" ] - then - local REAL_LUKS_KEYDEV="${LUKS_KEYDEV}" - if [ ! -e "${mntkey}${LUKS_KEY}" ] + REAL_LUKS_HEADERDEV=$(find_real_device "${LUKS_HEADERDEV}") + if [ -b "${REAL_LUKS_HEADERDEV}" ] then - REAL_LUKS_KEYDEV=$(find_real_device "${LUKS_KEYDEV}") - if [ -b "${REAL_LUKS_KEYDEV}" ] - then good_msg "Using key device ${REAL_LUKS_KEYDEV}." ${CRYPT_SILENT} - else - good_msg "Please insert removable device ${LUKS_KEYDEV} for ${LUKS_NAME}" ${CRYPT_SILENT} - # abort after 10 secs - local count=10 - while [ ${count} -gt 0 ] - do - count=$((count-1)) - sleep 1 - REAL_LUKS_KEYDEV=$(find_real_device "${LUKS_KEYDEV}") - if [ -b "${REAL_LUKS_KEYDEV}" ] - then - good_msg "Removable device ${REAL_LUKS_KEYDEV} detected." ${CRYPT_SILENT} - break - fi - done - if [ ! -b "${REAL_LUKS_KEYDEV}" ] + good_msg "Using header device ${REAL_LUKS_HEADERDEV}." ${CRYPT_SILENT} + else + good_msg "Please insert removable device ${LUKS_HEADERDEV} for ${LUKS_NAME}" ${CRYPT_SILENT} + # abort after 10 secs + local count=10 + while [ ${count} -gt 0 ] + do + count=$((count-1)) + sleep 1 + REAL_LUKS_HEADERDEV=$(find_real_device "${LUKS_HEADERDEV}") + if [ -b "${REAL_LUKS_HEADERDEV}" ] + then + good_msg "Removable device ${REAL_LUKS_HEADERDEV} detected." ${CRYPT_SILENT} + break + fi + done + if [ ! -b "${REAL_LUKS_HEADERDEV}" ] + then + eval CRYPT_${TYPE}_HEADER=${LUKS_HEADER} + bootstrapHeader ${TYPE} + eval LUKS_HEADERDEV='"${CRYPT_'${TYPE}'_HEADERDEV}"' + REAL_LUKS_HEADERDEV=$(find_real_device "${LUKS_HEADERDEV}") + if [ ! -b "${REAL_LUKS_HEADERDEV}" ] then - eval CRYPT_${TYPE}_KEY=${LUKS_KEY} - bootstrapKey ${TYPE} - eval LUKS_KEYDEV='"${CRYPT_'${TYPE}'_KEYDEV}"' - REAL_LUKS_KEYDEV=$(find_real_device "${LUKS_KEYDEV}") - if [ ! -b "${REAL_LUKS_KEYDEV}" ] - then - KEYDEV_ERROR=1 - bad_msg "Removable device ${LUKS_KEYDEV} not found." ${CRYPT_SILENT} - continue - fi - # continue otherwise will mount keydev which is mounted by bootstrap + HEADERDEV_ERROR=1 + bad_msg "Removable device ${LUKS_HEADERDEV} not found." ${CRYPT_SILENT} continue fi + # continue otherwise will mount headerdev which is mounted by bootstrap + continue fi + fi + + # At this point a device was recognized, now let's see if the header is there + [ ! -d "${mntheader}" ] && mkdir -p "${mntheader}" >/dev/null 2>&1 + + # determine fs -- 'auto' will not trigger module loading! + LUKS_HEADERDEV_FSTYPE=$(determine_fs "${REAL_LUKS_HEADERDEV}" "${LUKS_HEADERDEV_FSTYPE}") - # At this point a device was recognized, now let's see if the key is there - [ ! -d "${mntkey}" ] && mkdir -p "${mntkey}" >/dev/null 2>&1 + if ! run mount -n -t ${LUKS_HEADERDEV_FSTYPE} -o ro ${REAL_LUKS_HEADERDEV} ${mntheader} >/dev/null 2>&1 + then + HEADERDEV_ERROR=1 + bad_msg "Mounting of device ${REAL_LUKS_HEADERDEV} failed." ${CRYPT_SILENT} + continue + fi + + good_msg "Removable device ${REAL_LUKS_HEADERDEV} mounted." ${CRYPT_SILENT} + sleep 2 + + # headerfile exists? + if [ ! -e "${mntheader}${LUKS_HEADER}" ] + then + run umount -n "${mntheader}" >/dev/null 2>&1 + HEADER_ERROR=1 + HEADERDEV_ERROR=1 + bad_msg "Header {LUKS_HEADER} on device ${REAL_LUKS_HEADERDEV} not found." ${CRYPT_SILENT} + continue + fi + fi - mount -n -o ro ${REAL_LUKS_KEYDEV} ${mntkey} >/dev/null 2>&1 - if [ "$?" != '0' ] + if ! run cryptsetup isLuks ${LUKS_DEVICE} --header ${mntheader}${LUKS_HEADER} + then + bad_msg "The LUKS device ${LUKS_DEVICE} does not contain a LUKS header" ${CRYPT_SILENT} + DEV_ERROR=1 + continue + fi + + # At this point a candidate header exists (either mounted before or not) + good_msg "${LUKS_HEADER} on device ${REAL_LUKS_HEADERDEV} found" ${CRYPT_SILENT} + + cryptsetup_options="${cryptsetup_options} --header ${mntheader}${LUKS_HEADER}" + elif ! run cryptsetup isLuks ${LUKS_DEVICE} + then + bad_msg "The LUKS device ${LUKS_DEVICE} does not contain a LUKS header" ${CRYPT_SILENT} + DEV_ERROR=1 + continue + fi + + # Handle keys + if [ -n "${LUKS_KEY}" ] + then + local REAL_LUKS_KEYDEV="${LUKS_KEYDEV}" + if [ ! -e "${mntkey}${LUKS_KEY}" ] + then + REAL_LUKS_KEYDEV=$(find_real_device "${LUKS_KEYDEV}") + if [ -b "${REAL_LUKS_KEYDEV}" ] + then + good_msg "Using key device ${REAL_LUKS_KEYDEV}." ${CRYPT_SILENT} + else + good_msg "Please insert removable device ${LUKS_KEYDEV} for ${LUKS_NAME}" ${CRYPT_SILENT} + # abort after 10 secs + local count=10 + while [ ${count} -gt 0 ] + do + count=$((count-1)) + sleep 1 + REAL_LUKS_KEYDEV=$(find_real_device "${LUKS_KEYDEV}") + if [ -b "${REAL_LUKS_KEYDEV}" ] + then + good_msg "Removable device ${REAL_LUKS_KEYDEV} detected." ${CRYPT_SILENT} + break + fi + done + if [ ! -b "${REAL_LUKS_KEYDEV}" ] then - KEYDEV_ERROR=1 - bad_msg "Mounting of device ${REAL_LUKS_KEYDEV} failed." ${CRYPT_SILENT} - continue - else - good_msg "Removable device ${REAL_LUKS_KEYDEV} mounted." ${CRYPT_SILENT} - sleep 2 - # keyfile exists? - if [ ! -e "${mntkey}${LUKS_KEY}" ] + eval CRYPT_${TYPE}_KEY=${LUKS_KEY} + bootstrapKey ${TYPE} + eval LUKS_KEYDEV='"${CRYPT_'${TYPE}'_KEYDEV}"' + REAL_LUKS_KEYDEV=$(find_real_device "${LUKS_KEYDEV}") + if [ ! -b "${REAL_LUKS_KEYDEV}" ] then - umount -n "${mntkey}" >/dev/null 2>&1 - KEY_ERROR=1 KEYDEV_ERROR=1 - bad_msg "Key {LUKS_KEY} on device ${REAL_LUKS_KEYDEV} not found." ${CRYPT_SILENT} + bad_msg "Removable device ${LUKS_KEYDEV} not found." ${CRYPT_SILENT} continue fi + # continue otherwise will mount keydev which is mounted by bootstrap + continue fi fi - # At this point a candidate key exists (either mounted before or not) - good_msg "${LUKS_KEY} on device ${REAL_LUKS_KEYDEV} found" ${CRYPT_SILENT} - if [ "$(echo ${LUKS_KEY} | grep -o '.gpg$')" = ".gpg" ] + # At this point a device was recognized, now let's see if the key is there + [ ! -d "${mntkey}" ] && mkdir -p "${mntkey}" >/dev/null 2>&1 + + # determine fs -- 'auto' will not trigger module loading! + LUKS_KEYDEV_FSTYPE=$(determine_fs "${REAL_LUKS_KEYDEV}" "${LUKS_KEYDEV_FSTYPE}") + + if ! run mount -n -t ${LUKS_KEYDEV_FSTYPE} -o ro ${REAL_LUKS_KEYDEV} ${mntkey} >/dev/null 2>&1 + then + KEYDEV_ERROR=1 + bad_msg "Mounting of device ${REAL_LUKS_KEYDEV} failed." ${CRYPT_SILENT} + continue + fi + + good_msg "Removable device ${REAL_LUKS_KEYDEV} mounted." ${CRYPT_SILENT} + sleep 2 + + # keyfile exists? + if [ ! -e "${mntkey}${LUKS_KEY}" ] then - if [ ! -x '/sbin/gpg' ] + run umount -n "${mntkey}" >/dev/null 2>&1 + KEY_ERROR=1 + KEYDEV_ERROR=1 + bad_msg "Key {LUKS_KEY} on device ${REAL_LUKS_KEYDEV} not found." ${CRYPT_SILENT} + continue + fi + fi + + # At this point a candidate key exists (either mounted before or not) + good_msg "${LUKS_KEY} on device ${REAL_LUKS_KEYDEV} found" ${CRYPT_SILENT} + + if [ "$(echo ${LUKS_KEY} | grep -o '.gpg$')" = ".gpg" ] + then + if ! hash gpg >/dev/null 2>&1 + then + bad_msg "GPG-encrypted key file provided but gpg program is missing. Was initramfs built without --gpg parameter?" + bad_msg "Falling back to passphrase usage!" + else + [ -e /dev/tty ] && run mv /dev/tty /dev/tty.org + run mknod /dev/tty c 5 1 + ASK_PASS=1 + cryptsetup_options="${cryptsetup_options} -d -" + gpg_cmd="gpg --logger-file /dev/null --quiet" + # plymouth password entry is passed through STDIN, requiring '--passphrase-fd 0 --batch' + # for newer gpg versions (>=2.1) '--pinentry-mode loopback' may also be required for the above + # '--no-tty' is included to prevent interruption of plymouth by any gpg output + if [ "${PLYMOUTH}" = '1' -a "${CRYPT_SILENT}" != '1' ] then - bad_msg "GPG-encrypted key file provided but gpg program is missing. Was initramfs built without --gpg parameter?" - bad_msg "Falling back to passphrase usage!" + gpg_cmd="${gpg_cmd} --passphrase-fd 0 --batch --no-tty --decrypt ${mntkey}${LUKS_KEY} | " else - [ -e /dev/tty ] && mv /dev/tty /dev/tty.org - mknod /dev/tty c 5 1 - cryptsetup_options="${cryptsetup_options} -d -" - gpg_cmd="/sbin/gpg --logger-file /dev/null --quiet --decrypt ${mntkey}${LUKS_KEY} |" + gpg_cmd="${gpg_cmd} --decrypt ${mntkey}${LUKS_KEY} | " fi - else - cryptsetup_options="${cryptsetup_options} -d ${mntkey}${LUKS_KEY}" fi + else + cryptsetup_options="${cryptsetup_options} -d ${mntkey}${LUKS_KEY}" fi - # At this point, keyfile or not, we're ready! - crypt_filter "${gpg_cmd}cryptsetup ${cryptsetup_options} luksOpen ${LUKS_DEVICE} ${LUKS_NAME}" - crypt_filter_ret=$? + else + # no keyfile defined, password is required + ASK_PASS=1 + fi + + if [ -n "${cryptsetup_options}" ] + then + good_msg "Using the following cryptsetup options for ${LUKS_NAME}: ${cryptsetup_options}" ${CRYPT_SILENT} + fi - [ -e /dev/tty.org ] \ - && rm -f /dev/tty \ - && mv /dev/tty.org /dev/tty + # At this point, {header,key}file or not, we're ready! + crypt_filter "${gpg_cmd}cryptsetup ${cryptsetup_options} luksOpen ${LUKS_DEVICE} ${LUKS_NAME}" "${ASK_PASS}" + crypt_filter_ret=$? - if [ ${crypt_filter_ret} -eq 0 ] - then - touch "${OPENED_LOCKFILE}" - good_msg "LUKS device ${LUKS_DEVICE} opened" ${CRYPT_SILENT} - break - elif [ ! -e "${OPENED_LOCKFILE}" ] - then - bad_msg "Failed to open LUKS device ${LUKS_DEVICE}" ${CRYPT_SILENT} - DEV_ERROR=1 - KEY_ERROR=1 - KEYDEV_ERROR=1 - fi + [ -e /dev/tty.org ] \ + && run rm -f /dev/tty \ + && run mv /dev/tty.org /dev/tty + + if [ ${crypt_filter_ret} -eq 0 ] + then + run touch "${OPENED_LOCKFILE}" + good_msg "LUKS device ${LUKS_DEVICE} opened" ${CRYPT_SILENT} + break + elif [ ! -e "${OPENED_LOCKFILE}" ] + then + bad_msg "Failed to open LUKS device ${LUKS_DEVICE}" ${CRYPT_SILENT} + DEV_ERROR=1 + HEADER_ERROR=1 + HEADERDEV_ERROR=1 + KEY_ERROR=1 + KEYDEV_ERROR=1 fi fi done - umount "${mntkey}" >/dev/null 2>&1 - rmdir -p "${mntkey}" >/dev/null 2>&1 + + udevsettle + + if run mountpoint "${mntheader}" >/dev/null 2>&1 + then + run umount "${mntheader}" >/dev/null 2>&1 + fi + + if run mountpoint "${mntkey}" >/dev/null 2>&1 + then + run umount "${mntkey}" >/dev/null 2>&1 + fi + + [ -d "${mntheader}" ] && run rmdir -p "${mntheader}" >/dev/null 2>&1 + [ -d "${mntkey}" ] && run rmdir -p "${mntkey}" >/dev/null 2>&1 +} + +keyctl_keyadd() { + if [ -n "${KEYCTL_KEYDESC}" ] + then + if [ ! -x /bin/keyctl ] + then + bad_msg "keyctl program is missing. Was initramfs built without --keyctl parameter?" + exit 1 + fi + + # not using read to avoid secrets being left in memory + stty -echo + echo -n "Please type the key '${KEYCTL_KEYDESC}' for the user keyring then press Ctrl-D twice: " + KEYCTL_KEYID=`keyctl padd user "${KEYCTL_KEYDESC}" @u` + echo + stty echo + + if [ -n "${KEYCTL_KEYID}" -a -n "${KEYCTL_KEYTIMEOUT}" ] + then + keyctl timeout "${KEYCTL_KEYID}" "${KEYCTL_KEYTIMEOUT}" + fi + fi +} + +keyctl_keyremove() { + if [ -n "${KEYCTL_KEYID}" -a -z "${KEYCTL_KEYKEEP}" ] + then + if [ ! -x /bin/keyctl ] + then + bad_msg "keyctl program is missing. Was initramfs built without --keyctl parameter?" + exit 1 + fi + + keyctl revoke "${KEYCTL_KEYID}" + keyctl unlink "${KEYCTL_KEYID}" >/dev/null + + # trust but verify + if keyctl show "${KEYCTL_KEYID}" >/dev/null 2>&1 + then + # better reboot than leave the user passphrase accidentally exposed + bad_msg "unable to remove the newly added key from keyring, rebooting in 5 seconds for security" + sleep 5 + reboot -f + fi + + KEYCTL_KEYID= + fi } iface_name() { @@ -1585,7 +2262,9 @@ iface_name() { for interface in /sys/class/net/* do - if [ $(cat ${interface}/address) = "${mac}" ] + [ -e ${interface}/address ] || continue + + if [ "$(cat ${interface}/address 2>/dev/null)" = "${mac}" ] then echo ${interface##*/} return @@ -1597,6 +2276,16 @@ iface_name() { } start_network() { + good_msg "Starting network ..." + + if [ "${GK_HW_USE_MODULES_LOAD}" = '1' ] + then + # Load network modules only when we need them to avoid possible + # firmware problems for people not using network that early + modules_scan net + udevsettle + fi + # At least gk.net.iface can only be processed after sysfs was # mounted. local x= @@ -1620,17 +2309,21 @@ start_network() { GK_NET_GW=${x#*=} ;; gk.net.iface=*) - local tmp_iface=$(iface_name "${x#*=}") - if [ -z "${tmp_iface}" ] - then - warn_msg "Interface specified by '${x}' not found, falling back to genkernel defaults ..." - else - GK_NET_IFACE=${tmp_iface} - fi + GK_NET_IFACE=${x#*=} ;; gk.net.routes=*) GK_NET_ROUTES=${x#*=} ;; + gk.net.timeout.interface=*) + local tmp_interface_timeout=${x#*=} + if is_int "${tmp_interface_timeout}" + then + GK_NET_TIMEOUT_INTERFACE=${tmp_interface_timeout} + else + warn_msg "'${x}' does not look like a valid number -- will keep using default value ${GK_NET_TIMEOUT_INTERFACE}!" + fi + unset tmp_interface_timeout + ;; gk.net.timeout.dad=*) local tmp_dad_timeout=${x#*=} if is_int "${tmp_dad_timeout}" @@ -1664,53 +2357,148 @@ start_network() { esac done - if [ ! -d "/sys/class/net/${GK_NET_IFACE}" ] + local interface_identifier=device + if echo "${GK_NET_IFACE}" | grep -qE ':|-' then - warn_msg "Interface ${GK_NET_IFACE} not found; Will not try to start network ..." - return + interface_identifier=mac + good_msg_n "Waiting for interface with MAC address ${GK_NET_IFACE} ..." + else + good_msg_n "Waiting for interface ${GK_NET_IFACE} ..." + fi + + local tmp_interface= + local have_interface=0 + local interface_time_waited=0 + local interface_timeout_100msec_modulo= + local interface_timeout && let interface_timeout=$(date +%s)+1 + [ -n "${GK_NET_TIMEOUT_INTERFACE}" -a "${GK_NET_TIMEOUT_INTERFACE}" -gt 0 ] && let interface_timeout=${interface_timeout}+${GK_NET_TIMEOUT_INTERFACE}-1 + + while [ "${have_interface}" != '1' -a $(date +%s) -le ${interface_timeout} ] + do + tmp_interface=$(iface_name "${GK_NET_IFACE}") + if [ -n "${tmp_interface}" ] + then + # We got at least something to probe + if [ -d "/sys/class/net/${tmp_interface}" ] + then + GK_NET_IFACE="${tmp_interface}" + have_interface=1 + break + fi + fi + + if [ "${have_interface}" != '1' ] + then + let interface_time_waited=${interface_time_waited}+1 + sleep 0.1s + + let interface_timeout_100msec_modulo=${interface_time_waited}%10 + if [ ${interface_timeout_100msec_modulo} = 0 ] + then + is_quiet || printf "." + fi + fi + done + + is_quiet || echo + + if [ "${have_interface}" != '1' ] + then + # Timeout! + if [ "${interface_identifier}" = 'mac' ] + then + bad_msg "Interface with MAC address ${GK_NET_IFACE} not found!" + else + bad_msg "Interface ${GK_NET_IFACE} not found!" + fi + + warn_msg "Will not try to start network ..." + return 1 + elif [ "${interface_identifier}" = 'mac' ] + then + good_msg "Interface detected as ${GK_NET_IFACE}" fi if [ -z "${IP}" -o "${IP}" = 'dhcp' ] then - good_msg "Bringing up interface ${GK_NET_IFACE} using dhcp ..." ${QUIET} - busybox udhcpc -i "${GK_NET_IFACE}" -n -t ${GK_NET_DHCP_RETRIES} -T ${GK_NET_TIMEOUT_DHCP} -R -p "${GK_NET_DHCP_PIDFILE}" + if is_interface_up + then + # CONFIG_IP_PNP_DHCP and ip=dhcp probably caused kernel to bring up + # network for us. Really no need re-run dhcp... + warn_msg "Interface ${GK_NET_IFACE} is already up." + warn_msg "Skipping network setup; Will use existing network configuration ..." + run touch "${GK_NET_LOCKFILE}" + return 0 + fi + + local udhcpc_cmd="run udhcpc -i '${GK_NET_IFACE}' -n -t ${GK_NET_DHCP_RETRIES} -T ${GK_NET_TIMEOUT_DHCP} -R -p '${GK_NET_DHCP_PIDFILE}' 2>&1" + is_log_enabled && udhcpc_cmd="${udhcpc_cmd} | tee -a '${GK_INIT_LOG}'" + + good_msg "Bringing up interface ${GK_NET_IFACE} using dhcp ..." + eval "${udhcpc_cmd}" if [ $? -ne 0 ] then bad_msg "Failed to start udhcpc for interface ${GK_NET_IFACE}!" - return + return 1 + fi + elif echo "${IP}" | grep -qE ':|,' + then + if is_interface_up + then + # CONFIG_IP_PNP probably caused kernel to bring up + # network for us. Due to presence of ":" or "," + # we can assume an advanced network configuration + # which we cannot reproduce... + warn_msg "Interface ${GK_NET_IFACE} is already up." + warn_msg "Skipping network setup; Will use existing network configuration ..." + run touch "${GK_NET_LOCKFILE}" + return 0 fi + + warn_msg "Found advanced network configuration (check ip= kernel command-line argument)!" + warn_msg "Assuming user want to use kernel's IP PNP; Will not try to start network ..." + return 1 else - good_msg "Bringing up interface ${GK_NET_IFACE} ..." ${QUIET} - ip link set "${GK_NET_IFACE}" up + if is_interface_up + then + # At this point we don't know if kernel has brought up network the + # way we wanted. It's safer to restart interface and do it on our own... + warn_msg "Interface ${GK_NET_IFACE} is already up and therefore in an unknown state!" + warn_msg "Will now restart interface ${GK_NET_IFACE} to get into a known state ..." + kill_network + fi - good_msg "Setting address '${IP}' on ${GK_NET_IFACE} ..." ${QUIET} - ip addr add "${IP}" dev "${GK_NET_IFACE}" + good_msg "Bringing up interface ${GK_NET_IFACE} ..." + run ip link set "${GK_NET_IFACE}" up + + good_msg "Setting address '${IP}' on ${GK_NET_IFACE} ..." + run ip addr add "${IP}" dev "${GK_NET_IFACE}" if [ -n "${GK_NET_ROUTES}" ] then local route= for route in ${GK_NET_ROUTES} do - good_msg "Adding additional route '${route}' on ${GK_NET_IFACE} ..." ${QUIET} - ip route add "${route}" dev "${GK_NET_IFACE}" + good_msg "Adding additional route '${route}' on ${GK_NET_IFACE} ..." + run ip route add "${route}" dev "${GK_NET_IFACE}" done fi if [ -n "${GK_NET_GW}" ] then - good_msg "Adding default route via '${GK_NET_GW}' on ${GK_NET_IFACE} ..." ${QUIET} - ip route add default via "${GK_NET_GW}" dev "${GK_NET_IFACE}" + good_msg "Adding default route via '${GK_NET_GW}' on ${GK_NET_IFACE} ..." + run ip route add default via "${GK_NET_GW}" dev "${GK_NET_IFACE}" fi fi - touch "${GK_NET_LOCKFILE}" + run touch "${GK_NET_LOCKFILE}" } kill_network() { if [ -s "${GK_NET_DHCP_PIDFILE}" ] then good_msg "Stopping udhcpc ..." - kill $(cat "${GK_NET_DHCP_PIDFILE}") + run kill $(cat "${GK_NET_DHCP_PIDFILE}") fi if [ ! -d "/sys/class/net/${GK_NET_IFACE}" ] @@ -1728,8 +2516,7 @@ kill_network() { # interface. if ipv6_tentative then - [ -z "${QUIET}" ] && \ - printf "%b" "${WARN}**${NORMAL}${BOLD} Waiting for tentative IPv6 addresses to complete DAD ${NORMAL}..." + good_msg_n "Waiting for tentative IPv6 addresses to complete DAD ..." local dad_timeout=10 while [ ${dad_timeout} -gt 0 ] @@ -1737,11 +2524,10 @@ kill_network() { ipv6_tentative || break sleep 1 : $(( dad_timeout -= 1 )) - [ -z "${QUIET}" ] && \ - printf "." + is_quiet || printf "." done - echo "" + is_quiet || echo if [ ${dad_timeout} -le 0 ] then @@ -1749,26 +2535,24 @@ kill_network() { fi fi - [ -z "${QUIET}" ] && \ - printf "%b" "${GOOD}>>${NORMAL}${BOLD} Bringing down interface ${GK_NET_IFACE} ${NORMAL}..." + good_msg_n "Bringing down interface ${GK_NET_IFACE} ..." local deconfiguration_timeout=${GK_NET_TIMEOUT_DECONFIGURATION} while [ ${deconfiguration_timeout} -gt 0 ] do - ip addr flush dev "${GK_NET_IFACE}" - ip route flush dev "${GK_NET_IFACE}" - ip link set "${GK_NET_IFACE}" down + run ip addr flush dev "${GK_NET_IFACE}" + run ip route flush dev "${GK_NET_IFACE}" + run ip link set "${GK_NET_IFACE}" down if grep -q "down" "/sys/class/net/${GK_NET_IFACE}/operstate" 2>/dev/null then break fi sleep 1 : $(( deconfiguration_timeout -= 1 )) - [ -z "${QUIET}" ] && \ - printf "." + is_quiet || printf "." done - echo "" + is_quiet || echo if [ ${deconfiguration_timeout} -le 0 ] then @@ -1776,7 +2560,16 @@ kill_network() { return fi - rm "${GK_NET_LOCKFILE}" + [ -f "${GK_NET_LOCKFILE}" ] && run rm "${GK_NET_LOCKFILE}" +} + +is_interface_up() { + if ip link show dev "${GK_NET_IFACE}" 2>/dev/null | grep -q ',UP,' + then + return 0 + else + return 1 + fi } ipv6_tentative() { @@ -1788,12 +2581,23 @@ ipv6_tentative() { fi } -start_LUKS() { - # if key is set but neither ssh enabled or key device is given, find - # the key device +start_LUKS_root() { + # if key/header is set but neither ssh enabled or key device is given, find + # the key/header device - [ -n "${CRYPT_ROOT_KEY}" ] && [ -z "${CRYPT_ROOT_KEYDEV}" ] \ - && sleep 6 && bootstrapKey "ROOT" + if ( + [ -n "${CRYPT_ROOT_KEY}" -a -z "${CRYPT_ROOT_KEYDEV}" ] || + [ -n "${CRYPT_ROOT_HEADER}" -a -z "${CRYPT_ROOT_HEADERDEV}" ] + ) + then + sleep 6 + fi + + [ -n "${CRYPT_ROOT_KEY}" -a -z "${CRYPT_ROOT_KEYDEV}" ] \ + && bootstrapKey "ROOT" + + [ -n "${CRYPT_ROOT_HEADER}" -a -z "${CRYPT_ROOT_HEADERDEV}" ] \ + && bootstrapHeader "ROOT" if [ -n "${CRYPT_ROOT}" ] then @@ -1806,10 +2610,24 @@ start_LUKS() { REAL_ROOT="/dev/mapper/root" fi fi +} +start_LUKS_swap() { # same for swap, but no need to sleep if root was unencrypted - [ -n "${CRYPT_SWAP_KEY}" ] && [ -z "${CRYPT_SWAP_KEYDEV}" ] \ - && { [ -z "${CRYPT_ROOT}" ] && sleep 6; bootstrapKey "SWAP"; } + + if ( + [ -n "${CRYPT_SWAP_KEY}" -a -z "${CRYPT_SWAP_KEYDEV}" ] || + [ -n "${CRYPT_SWAP_HEADER}" -a -z "${CRYPT_SWAP_HEADERDEV}" ] + ) + then + [ -z "${CRYPT_ROOT}" ] && sleep 6 + fi + + [ -n "${CRYPT_SWAP_KEY}" -a -z "${CRYPT_SWAP_KEYDEV}" ] \ + && bootstrapKey "SWAP" + + [ -n "${CRYPT_SWAP_HEADER}" -a -z "${CRYPT_SWAP_HEADERDEV}" ] \ + && bootstrapHeader "SWAP" if [ -n "${CRYPT_SWAP}" ] then @@ -1831,30 +2649,39 @@ start_sshd() { if [ ! -f "${GK_NET_LOCKFILE}" ] then - warn_msg "Network not started; Not starting sshd ..." + warn_msg "Network not started; Not starting sshd ..." 0 return fi - if [ ! -x "/usr/sbin/dropbear" ] + if ! hash dropbear >/dev/null 2>&1 then - bad_msg "/usr/sbin/dropbear not found! Did you call genkernel with --ssh parameter?" + bad_msg "/usr/sbin/dropbear not found! Was initramfs built without --ssh parameter?" return fi - # setup environment variables for the ssh login shell - local varname= varvalue= - touch "${CRYPT_ENV_FILE}" - for varname in CRYPT_ROOT CRYPT_ROOT_TRIM CRYPT_SILENT CRYPT_SWAP DEBUG - do - eval varvalue=\$${varname} - echo "${varname}=${varvalue}" >> "${CRYPT_ENV_FILE}" - done + # setup environment variables for the remote rescue shell + # ZFS will use a different file because $REAL_ROOT for ZFS + # isn't known yet. + write_env_file \ + "${CRYPT_ENV_FILE}" \ + CRYPT_ROOT \ + CRYPT_ROOT_OPTIONS \ + CRYPT_SILENT \ + CRYPT_SWAP \ + CRYPT_SWAP_OPTIONS - touch /var/log/lastlog + run touch /var/log/lastlog - good_msg "Starting dropbear sshd ..." ${QUIET} - /usr/sbin/dropbear -p ${GK_SSHD_PORT} -R -P "${GK_SSHD_PIDFILE}" 2>/var/log/dropbear.log - test_success "Failed to start dropbear" + good_msg "Starting dropbear sshd ..." + run dropbear -p ${GK_SSHD_PORT} -R -P "${GK_SSHD_PIDFILE}" 2>/run/initramfs/dropbear.log + if [ $? -eq 0 ] + then + if [ "${GK_PROMPT_TIMEOUT}" = '0' ] + then + warn_msg "Changing gk.prompt.timeout=0 to 30 to allow remote user to answer prompts ..." + GK_PROMPT_TIMEOUT=30 + fi + fi } wait_sshd() { @@ -1868,7 +2695,10 @@ wait_sshd() { return fi - printf "%b" "${GOOD}>>${NORMAL}${BOLD} gk.sshd.wait set; Waiting ${GK_SSHD_WAIT} seconds for SSH connection ${NORMAL}..." + good_msg_n "gk.sshd.wait set; Waiting ${GK_SSHD_WAIT} seconds for SSH connection ..." + + local last_cmd="run last -W 2>/dev/null | head -n 3" + is_log_enabled && last_cmd="${last_cmd} | tee -a '${GK_INIT_LOG}'" local ssh_timeout=${GK_SSHD_WAIT} while [ ${ssh_timeout} -gt 0 ] @@ -1876,22 +2706,22 @@ wait_sshd() { if [ -f "${GK_SSHD_LOCKFILE}" ] then echo "" - last -W | head -n 3 2>/dev/null + eval "${last_cmd}" break fi sleep 1 : $(( ssh_timeout -= 1 )) - printf "." + is_quiet || printf "." done - echo "" + is_quiet || echo } kill_sshd() { if [ -s "${GK_SSHD_PIDFILE}" ] then - good_msg "Stopping dropbear sshd ..." ${QUIET} - kill $(cat "${GK_SSHD_PIDFILE}") + good_msg "Stopping dropbear sshd ..." + run kill $(cat "${GK_SSHD_PIDFILE}") fi } @@ -1900,8 +2730,8 @@ cleanup() { then if [ -f "${GK_SSHD_LOCKFILE}" ] then - warn_msg "The lockfile at '${GK_SSHD_LOCKFILE}' exists." - warn_msg "The boot process will be paused until the lock is removed." + warn_msg "The lockfile at '${GK_SSHD_LOCKFILE}' exists." 0 + warn_msg "The boot process will be paused until the lock is removed." 0 while true do if [ -f "${GK_SSHD_LOCKFILE}" ] @@ -1917,11 +2747,19 @@ cleanup() { kill_sshd # Ensure that we terminated any existing connection - pkill -9 dropbear >/dev/null 2>&1 + if pgrep dropbear >/dev/null 2>&1 + then + run pkill -9 dropbear >/dev/null 2>&1 + fi if [ -f "${GK_NET_LOCKFILE}" ] then - kill_network + if [ "${REAL_ROOT}" = "/dev/nfs" ] + then + warn_msg "real_root=/dev/nfs; Will not bring down interface ${GK_NET_IFACE} ..." + else + kill_network + fi fi } @@ -1929,12 +2767,12 @@ sdelay() { # Sleep a specific number of seconds if SDELAY is set if [ -n "${SDELAY}" ] then - good_msg_n "Waiting ${SDELAY} seconds ..." + good_msg_n "scandelay set; Waiting ${SDELAY} seconds ..." while [ ${SDELAY} -gt 0 ] do let SDELAY=${SDELAY}-1 sleep 1 - printf "." + is_quiet || printf "." done echo elif [ "${CDROOT}" = '1' ] @@ -1943,16 +2781,6 @@ sdelay() { fi } -quiet_kmsg() { - # if QUIET is set make the kernel less chatty - [ -n "${QUIET}" ] && echo '0' > /proc/sys/kernel/printk -} - -verbose_kmsg() { - # if QUIET is set make the kernel less chatty - [ -n "${QUIET}" ] && echo '6' > /proc/sys/kernel/printk -} - cdupdate() { if [ "${CDROOT}" = '1' ] then @@ -1965,11 +2793,11 @@ cdupdate() { if [ -n "${cdupdate_path}" ] then good_msg "Running cdupdate.sh (${cdupdate_path})" - ${cdupdate_path} + run ${cdupdate_path} if [ "$?" != '0' ] then bad_msg "Executing cdupdate.sh failed!" - run_shell + run_emergency_shell fi else good_msg 'No cdupdate.sh script found, skipping ...' @@ -1977,52 +2805,42 @@ cdupdate() { fi } -setup_btrfsctl() { - # start BTRFS volume detection, if available - [ -x /sbin/btrfsctl ] && /sbin/btrfsctl -a -} - -setup_md_device() { - local device - - [ -z "$1" ] && device="${REAL_ROOT}" || device="$1" - [ -z "${device}" ] && return # LiveCD - - if [ $(echo ${device}|sed -e 's#\(luks:\)\?\(/dev/md\)[[:digit:]]\+#\2#') = "/dev/md" ] +do_resume() { + local device=$(find_real_device "${REAL_RESUME}") + if [ -z "${device}" ] then - good_msg 'Detected real_root as a md device. Setting up the device node ...' - MD_NUMBER=$(echo ${device}|sed -e 's#\(luks:\)\?/dev/md\([[:digit:]]\+\)#\2#') - if [ ! -e /dev/md${MD_NUMBER} ] - then - mknod /dev/md${MD_NUMBER} b 9 ${MD_NUMBER} >/dev/null 2>&1 - [ $? -ne 0 ] && bad_msg "Creation of /dev/md${MD_NUMBER} failed ..." - fi - mdstart ${MDPART} /dev/md${MD_NUMBER} + warn_msg "resume device (${REAL_RESUME}) not found; Skipping resume ..." 0 + return 0 + else + REAL_RESUME="${device}" fi -} -rundebugshell() { - if [ -n "${DEBUG}" ] + local resume_tried=no + if [ -d /proc/suspend2 -o -d /sys/power/suspend2 -o -d /sys/power/tuxonice ] + then + tuxonice_resume + resume_tried=yes + elif [ -f /sys/power/resume ] then - good_msg 'Starting debug shell as requested by "debug" option.' - good_msg "Stopping by: ${1}" - run_shell + swsusp_resume + resume_tried=yes fi -} -do_resume() { - if [ -d /proc/suspend2 -o -d /sys/power/suspend2 -o -d /sys/power/tuxonice ] + if is_true "${resume_tried}" then - tuxonice_resume + warn_msg "System is not resuming from ${REAL_RESUME}, probably because it wasn't suspended; Continue normal booting ..." else - swsusp_resume + warn_msg "resume device (${REAL_RESUME}) specified but kernel is lacking proper hibernation support!" fi } swsusp_resume() { # determine swap resume partition local device=$(ls -lL "${REAL_RESUME}" | sed 's/\ */ /g' | cut -d \ -f 5-6 | sed 's/,\ */:/') - [ -f /sys/power/resume ] && echo "${device}" > /sys/power/resume + + log_msg "Trying to resume using swsusp ..." + log_msg "COMMAND: 'echo \"${device}\" > /sys/power/resume'" + echo "${device}" > /sys/power/resume } tuxonice_resume() { @@ -2069,20 +2887,24 @@ tuxonice_resume() { if ! grep suspend_noui /proc/cmdline >/dev/null 2>&1 then - which suspend2ui_text >/dev/null 2>&1 && which suspend2ui_text > "${tuxonice_userui_program}" - which tuxoniceui_text >/dev/null 2>&1 && which tuxoniceui_text > "${tuxonice_userui_program}" + command -v suspend2ui_text >/dev/null 2>&1 && command -v suspend2ui_text > "${tuxonice_userui_program}" + command -v tuxoniceui_text >/dev/null 2>&1 && command -v tuxoniceui_text > "${tuxonice_userui_program}" if [ -n "${splash_theme}" ] then ln -s /etc/splash/${splash_theme} /etc/splash/suspend2 ln -s /etc/splash/${splash_theme} /etc/splash/tuxonice - which suspend2ui_fbsplash >/dev/null 2>&1 && which suspend2ui_fbsplash > "${tuxonice_userui_program}" - which tuxoniceui_fbsplash >/dev/null 2>&1 && which tuxoniceui_fbsplash > "${tuxonice_userui_program}" + command -v suspend2ui_fbsplash >/dev/null 2>&1 && command -v suspend2ui_fbsplash > "${tuxonice_userui_program}" + command -v tuxoniceui_fbsplash >/dev/null 2>&1 && command -v tuxoniceui_fbsplash > "${tuxonice_userui_program}" fi fi + + log_msg "Trying to resume using TuxOnIce ..." + log_msg "COMMAND: 'echo \"${REAL_RESUME}\" > ${tuxonice_resumedev}'" echo "${REAL_RESUME}" > "${tuxonice_resumedev}" + log_msg "COMMAND: 'echo > ${tuxonice_do_resume}'" echo > "${tuxonice_do_resume}" } @@ -2113,16 +2935,16 @@ setup_squashfs_aufs() { for dir in ${aufs_rw_branch} ${aufs_ro_branch} do - [ ! -d "${dir}" ] && mkdir -p "${dir}" + [ ! -d "${dir}" ] && run mkdir -p "${dir}" done good_msg "Loading aufs module ..." - modprobe aufs >/dev/null 2>&1 + run modprobe aufs >/dev/null 2>&1 checkfs aufs - mount -t squashfs -o loop,ro "${CDROOT_PATH}/${LOOPEXT}${LOOP}" "${aufs_ro_branch}" - mount -t tmpfs none "${aufs_rw_branch}" - mount -t aufs -o "br:${aufs_rw_branch}:${aufs_ro_branch}" aufs "${NEW_ROOT}" + run mount -t squashfs -o loop,ro "${CDROOT_PATH}/${LOOPEXT}${LOOP}" "${aufs_ro_branch}" + run mount -t tmpfs none "${aufs_rw_branch}" + run mount -t aufs -o "br:${aufs_rw_branch}:${aufs_ro_branch}" aufs "${NEW_ROOT}" } setup_unionfs() { @@ -2141,9 +2963,9 @@ setup_unionfs() { # fi # mkdir -p ${MEMORY} - mkdir -p ${UNION} + run mkdir -p ${UNION} good_msg "Loading fuse module" - modprobe fuse >/dev/null 2>&1 + run modprobe fuse >/dev/null 2>&1 # if [ -n "${UNIONFS}" ] # then # CHANGESDEV=${UNIONFS} @@ -2170,12 +2992,12 @@ setup_unionfs() { # mount -t tmpfs tmpfs ${MEMORY} # fi - mkdir /tmp - mkdir -p ${UNION} + run mkdir /tmp + run mkdir -p ${UNION} # mkdir -p ${CHANGES} # mount -t unionfs -o dirs=${CHANGES}=rw unionfs ${UNION} good_msg "Creating union mount" - unionfs -o allow_other,cow,noinitgroups,suid,dev,default_permissions,use_ino ${rw_dir}=RW:${ro_dir}=RO ${UNION} 2>/dev/null + run unionfs -o allow_other,cow,noinitgroups,suid,dev,default_permissions,use_ino ${rw_dir}=RW:${ro_dir}=RO ${UNION} 2>/dev/null ret=$? if [ ${ret} -ne 0 ] then @@ -2183,12 +3005,27 @@ setup_unionfs() { USE_UNIONFS_NORMAL=0 fi [ ! -d "${NEW_ROOT}${CDROOT_PATH}" ] && mkdir -p "${NEW_ROOT}${CDROOT_PATH}" - mount --bind "${CDROOT_PATH}" "${NEW_ROOT}${CDROOT_PATH}" + run mount --bind "${CDROOT_PATH}" "${NEW_ROOT}${CDROOT_PATH}" else USE_UNIONFS_NORMAL=0 fi } +get_active_console() { + local active_console=console + + while [ -f /sys/class/tty/${active_console}/active ] + do + active_console=$(cat /sys/class/tty/${active_console}/active) + + # last console will be the active one, + # see https://www.kernel.org/doc/html/latest/admin-guide/serial-console.html + active_console=${active_console##* } + done + + echo ${active_console} +} + get_mounts_list() { awk ' /^[[:blank:]]*#/ { next } @@ -2220,6 +3057,24 @@ get_mount_device() { ' ${NEW_ROOT}/etc/fstab } +get_zfs_property() { + local device=${1} + local propertyname=${2} + + echo "$(zfs get -H -o value ${propertyname} "${device}" 2>/dev/null)" +} + +# Returns TRUE if $1 contains literal string $2 at the +# beginning and is not empty +str_starts() { + if [ "${1#"${2}"*}" != "${1}" ] + then + return 0 + fi + + return 1 +} + # If the kernel is handed a mount option is does not recognize, it WILL fail to # mount. util-linux handles auto/noauto, but busybox passes it straight to the kernel # which then rejects the mount. |