summaryrefslogtreecommitdiffstats
path: root/scripts/functions
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scripts/functions688
1 files changed, 688 insertions, 0 deletions
diff --git a/scripts/functions b/scripts/functions
new file mode 100644
index 0000000..60f9195
--- /dev/null
+++ b/scripts/functions
@@ -0,0 +1,688 @@
+# -*- shell-script -*-
+
+_log_msg()
+{
+ if [ "${quiet?}" = "y" ]; then return; fi
+ # shellcheck disable=SC2059
+ printf "$@"
+ return 0 # Prevents error carry over in case of unavailable console
+}
+
+log_success_msg()
+{
+ _log_msg "Success: %s\\n" "$*"
+}
+
+log_failure_msg()
+{
+ _log_msg "Failure: %s\\n" "$*"
+}
+
+log_warning_msg()
+{
+ _log_msg "Warning: %s\\n" "$*"
+}
+
+log_begin_msg()
+{
+ _log_msg "Begin: %s ... " "$*"
+}
+
+log_end_msg()
+{
+ _log_msg "done.\\n"
+}
+
+panic()
+{
+ local console rest IFS
+
+ if command -v chvt >/dev/null 2>&1; then
+ chvt 1
+ fi
+
+ echo "$@"
+
+ # The panic= parameter implies we should disallow console access
+ if [ -n "${panic?}" ]; then
+ delay=
+ case "${panic?}" in
+ -*[![:digit:].]*) # invalid: wait forever
+ ;;
+ -*) # timeout < 0: reboot immediately
+ delay=0
+ ;;
+ 0 | *[![:digit:].]*) # timeout = 0 or invalid: wait forever
+ ;;
+ *) # timeout > 0: seconds before rebooting
+ delay="${panic}"
+ ;;
+ esac
+ if [ -n "${delay}" ]; then
+ echo "Rebooting automatically due to panic= boot argument"
+ sleep "${delay}"
+ reboot -f
+ else
+ echo "Halting automatically due to panic= boot argument"
+ halt -f
+ fi
+ exit # in case reboot fails, force kernel panic
+ fi
+
+ run_scripts /scripts/panic
+
+ # Try to use setsid, which will enable job control in the shell
+ # and paging in more
+ if command -v setsid >/dev/null 2>&1; then
+ unset IFS
+ read -r console rest </proc/consoles
+ if [ "${console}" = "tty0" ]; then
+ # Need to choose a specific VT
+ console="tty1"
+ fi
+ # We don't have 'setsid -c' so we need to setsid, open
+ # the tty, and finally exec an interactive shell
+ REASON="$*" PS1='(initramfs) ' setsid sh -c "exec sh -i <>/dev/${console} 1>&0 2>&1"
+ else
+ REASON="$*" PS1='(initramfs) ' sh -i </dev/console >/dev/console 2>&1
+ fi
+}
+
+maybe_break()
+{
+ case ",${break?}," in
+ *,$1,*)
+ if [ "$1" = "top" ]; then
+ # udev is not yet running, so load keyboard drivers
+ if [ "${quiet}" = "y" ]; then
+ opts="-q"
+ else
+ opts="-v"
+ fi
+ /sbin/modprobe ${opts} -a i8042 atkbd ehci-pci ehci-orion \
+ ehci-hcd ohci-hcd ohci-pci uhci-hcd usbhid xhci \
+ xhci-pci xhci-hcd
+ sleep 2
+ for modalias in /sys/bus/hid/devices/*/modalias; do
+ if [ -f "${modalias}" ]; then
+ /sbin/modprobe ${opts} -b "$(cat "${modalias}")"
+ fi
+ done
+ fi
+ panic "Spawning shell within the initramfs"
+ ;;
+ esac
+}
+
+# For boot time only; this is overridden at build time in hook-functions
+run_scripts()
+{
+ initdir=${1}
+ [ ! -d "${initdir}" ] && return
+
+ shift
+ . "${initdir}/ORDER"
+}
+
+# Load custom modules first
+load_modules()
+{
+ if [ -e /conf/modules ]; then
+ while read -r m; do
+ # Skip empty lines
+ if [ -z "$m" ]; then
+ continue
+ fi
+ # Skip comments - d?ash removes whitespace prefix
+ com=$(printf "%.1s" "${m}")
+ if [ "$com" = "#" ]; then
+ continue
+ fi
+ # shellcheck disable=SC2086
+ /sbin/modprobe $m
+ done < /conf/modules
+ fi
+}
+
+_uptime() {
+ local uptime
+ uptime="$(cat /proc/uptime)"
+ uptime="${uptime%%[. ]*}"
+ echo "$uptime"
+}
+
+time_elapsed() {
+ if [ -z "${starttime-}" ]; then
+ log_failure_msg "time_elapsed() called before \$starttime initialized"
+ echo 0
+ fi
+ local delta
+ delta="$(_uptime)"
+ delta=$((delta - starttime))
+ echo "$delta"
+}
+
+# lilo compatibility
+parse_numeric() {
+ case $1 in
+ *:*)
+ # Does it match /[0-9]*:[0-9]*/?
+ minor=${1#*:}
+ major=${1%:*}
+ case $major$minor in
+ *[!0-9]*)
+ # No.
+ return
+ ;;
+ esac
+ ;;
+ "" | *[!A-Fa-f0-9]*)
+ # "", "/*", etc.
+ return
+ ;;
+ *)
+ # [A-Fa-f0-9]*
+ value=$(( 0x${1} ))
+ minor=$(( (value & 0xff) | (value >> 12) & 0xfff00 ))
+ major=$(( (value >> 8) & 0xfff ))
+ ;;
+ esac
+
+ # shellcheck disable=SC2034
+ ROOT="/dev/block/${major}:${minor}"
+}
+
+# Parameter: device node to check
+# Echos fstype to stdout
+# Return value: indicates if an fs could be recognized
+get_fstype ()
+{
+ local FS FSTYPE
+ FS="${1}"
+
+ # blkid has a more complete list of file systems,
+ # but fstype is more robust
+ FSTYPE="unknown"
+ eval "$(fstype "${FS}" 2> /dev/null)"
+ if [ "$FSTYPE" = "unknown" ]; then
+ FSTYPE=$(blkid -o value -s TYPE "${FS}") || return
+ fi
+ echo "${FSTYPE}"
+ return 0
+}
+
+_set_netdev_from_ip_param()
+{
+ # If the ip= parameter is present and specifies a device, use
+ # that in preference to any device name we already have
+ local IFS=:
+ set -f
+ # shellcheck disable=SC2086
+ set -- ${IP-}
+ set +f
+ if [ -n "${6-}" ]; then
+ DEVICE="$6"
+ return 0
+ fi
+ return 1
+}
+
+_set_netdev_from_hw_address()
+{
+ local want_address="$1"
+ local device
+ for device in /sys/class/net/*; do
+ if [ -f "$device/address" ] &&
+ [ "$(cat "$device/address")" = "$want_address" ]; then
+ DEVICE="${device##*/}"
+ return 0
+ fi
+ done
+ return 1
+}
+
+_usable_netdev_exists()
+{
+ # Look for a device with IFF_LOOPBACK clear and (IFF_BROADCAST
+ # or IFF_POINTTOPOINT) set. This is the same test the kernel
+ # and ipconfig apply to find a device.
+ local device
+ local flags
+ for device in /sys/class/net/*; do
+ if [ -f "${device}/flags" ]; then
+ flags="$(cat "${device}/flags")"
+ if [ "$((flags & 8))" -eq 0 ] &&
+ [ "$((flags & 0x12))" -ne 0 ]; then
+ return 0
+ fi
+ fi
+ done
+ return 1
+}
+
+_update_ip_param()
+{
+ # If the ip= parameter is present, and is a colon-separated list,
+ # but does not specify a device, substitute in the device name
+ # we have
+ local IFS=:
+ set -f
+ # shellcheck disable=SC2086
+ set -- ${IP}
+ set +f
+ if [ -z "${6-}" ] && [ $# -ge 2 ] && [ -n "${DEVICE-}" ]; then
+ IP="$1:$2:$3:$4:$5:${DEVICE}"
+ shift 6 || shift $#
+ IP="${IP}:$*"
+ fi
+}
+
+configure_networking()
+{
+ local netdev_desc
+
+ # The order of precedence here is:
+ # 1. Device specified by ip= kernel parameter
+ # 2. Device matching MAC specified by BOOTIF= kernel parameter
+ # 3. Build-time DEVICE variable
+ # In case 2 we only discover the device name while waiting
+ # for a device.
+ if _set_netdev_from_ip_param; then
+ netdev_desc="${DEVICE}"
+ elif [ -n "${BOOTIF-}" ]; then
+ # pxelinux sets BOOTIF to a value based on the mac address of the
+ # network card used to PXE boot
+ # pxelinux sets BOOTIF to 01-$mac_address
+
+ # strip off the leading "01-", which isn't part of the mac
+ # address
+ temp_mac=${BOOTIF#*-}
+
+ # convert to typical mac address format by replacing "-" with ":"
+ bootif_mac=""
+ IFS='-'
+ for x in $temp_mac ; do
+ if [ -z "$bootif_mac" ]; then
+ bootif_mac="$x"
+ else
+ bootif_mac="$bootif_mac:$x"
+ fi
+ done
+ unset IFS
+
+ _set_netdev_from_hw_address "${bootif_mac}"
+ netdev_desc="device with address ${bootif_mac}"
+ elif [ -n "${DEVICE-}" ]; then
+ netdev_desc="${DEVICE}"
+ else
+ netdev_desc="any network device"
+ fi
+
+ # networking already configured thus bail out
+ [ -n "${DEVICE-}" ] && [ -e /run/net-"${DEVICE}".conf ] && return 0
+
+ local netdevwait=180
+ log_begin_msg "Waiting up to ${netdevwait} secs for ${netdev_desc} to become available"
+ while true; do
+ if [ "$(time_elapsed)" -ge "$netdevwait" ]; then
+ log_failure_msg "Network device did not appear in time"
+ break
+ fi
+ if [ -n "${DEVICE-}" ]; then
+ [ -e "/sys/class/net/${DEVICE}" ] && break
+ elif [ -n "${bootif_mac-}" ]; then
+ _set_netdev_from_hw_address "${bootif_mac}" && break
+ else
+ _usable_netdev_exists && break
+ fi
+ sleep 1
+ done
+ log_end_msg
+
+ _update_ip_param
+
+ wait_for_udev 10
+
+ # support ip options see linux sources
+ # Documentation/filesystems/nfs/nfsroot.txt
+ # Documentation/frv/booting.txt
+
+ for ROUNDTTT in 2 3 4 6 9 16 25 36 64 100; do
+
+ # The NIC is to be configured if this file does not exist.
+ # Ip-Config tries to create this file and when it succeds
+ # creating the file, ipconfig is not run again.
+ for x in /run/net-"${DEVICE-}".conf /run/net-*.conf ; do
+ [ -e "$x" ] && break 2
+ done
+
+ case "${IP-}" in
+ none|off)
+ # Do nothing
+ ;;
+ ""|on|any)
+ # Bring up device
+ ipconfig -t ${ROUNDTTT} "${DEVICE}"
+ ;;
+ dhcp|bootp|rarp|both)
+ ipconfig -t ${ROUNDTTT} -c "${IP}" -d "${DEVICE}"
+ ;;
+ *)
+ ipconfig -t ${ROUNDTTT} -d "$IP"
+ ;;
+ esac
+ done
+
+ # source ipconfig output (first try specific bootdevice, then any
+ # interface...). ipconfig should have quit after first response
+ local file
+ for file in "/run/net-${DEVICE-}.conf" /run/net-*.conf; do
+ if [ -e "$file" ]; then
+ . "$file"
+ break
+ fi
+ done
+
+ netinfo_to_resolv_conf /etc/resolv.conf "/run/net-${DEVICE-}.conf" /run/net-*.conf
+
+ # shellcheck disable=SC2169
+ if test -n "${HOSTNAME-}"; then
+ # shellcheck disable=SC2169
+ persist_hostname "${HOSTNAME}" "${DNSDOMAIN-}"
+ fi
+}
+
+# Write resolv.conf from /run/net-<device> style files.
+# $1=resolv.conf path (print to stdout if "-" is specified)
+# remaining parameters: /run/net-<device> style files
+netinfo_to_resolv_conf()
+{
+ local domain="" n="" nameservers="" resolv_conf="" search="" output="$1" CR="
+"
+ shift
+
+ for net_file in "$@"; do
+ # Define sourced variables as local to avoid causing side effects.
+ # shellcheck disable=SC2034
+ local DEVICE PROTO IPV4ADDR IPV4BROADCAST IPV4NETMASK IPV4ROUTE0SUBNET \
+ IPV4ROUTE0GATEWAY IPV4ROUTE1SUBNET IPV4ROUTE1GATEWAY IPV4GATEWAY \
+ IPV4DNS0 IPV4DNS1 HOSTNAME DNSDOMAIN NISDOMAIN ROOTSERVER ROOTPATH \
+ filename UPTIME DHCPLEASETIME DOMAINSEARCH
+ [ -e "$net_file" ] || continue
+ . "$net_file" || { echo "Error: Failed to source $net_file" >&2; return 1; }
+ if test -n "${DNSDOMAIN}"; then
+ domain="${DNSDOMAIN}"
+ fi
+ for n in "${IPV4DNS0}" "${IPV4DNS1}"; do
+ if test -z "$n" -o "$n" = 0.0.0.0; then
+ continue
+ fi
+ # Remove duplicates
+ case " ${nameservers} " in
+ *\ $n\ *)
+ continue ;;
+ esac
+ nameservers="${nameservers} ${n}"
+ done
+ for n in ${DOMAINSEARCH}; do
+ [ -n "$n" ] || continue
+ # Remove duplicates
+ case " ${search} " in
+ *\ $n\ *)
+ continue ;;
+ esac
+ search="${search} ${n}"
+ done
+ done
+
+ # Construct content for resolv.conf
+ if test -n "${domain}"; then
+ resolv_conf="${resolv_conf}domain ${domain}${CR}"
+ fi
+ for n in ${nameservers}; do
+ resolv_conf="${resolv_conf}nameserver $n${CR}"
+ done
+ if test -n "${search}"; then
+ resolv_conf="${resolv_conf}search ${search# }${CR}"
+ fi
+
+ if test -z "$resolv_conf"; then
+ echo "no search or nameservers found in $*" 1>&2
+ return
+ fi
+ if test "$output" = "-"; then
+ printf "%s" "$resolv_conf"
+ else
+ printf "%s" "$resolv_conf" > "$output"
+ fi
+}
+
+_generate_hosts_content()
+{
+ local hostname="$1"
+ local domain="$2"
+ local fqdn
+
+ if test -n "${domain}"; then
+ fqdn="${hostname}.${domain} "
+ else
+ fqdn=""
+ fi
+ cat <<EOF
+127.0.0.1 localhost
+127.0.1.1 ${fqdn}${hostname}
+
+# The following lines are desirable for IPv6 capable hosts
+::1 localhost ip6-localhost ip6-loopback
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+EOF
+}
+
+# Write hostname into /etc/hostname and /etc/hosts
+# $1=hostname
+# $2=domain (optional)
+persist_hostname()
+{
+ local hostname="$1"
+ local domain="${2-}"
+
+ echo "$hostname" > /etc/hostname
+ echo "$hostname" > /proc/sys/kernel/hostname
+ _generate_hosts_content "$hostname" "$domain" > /etc/hosts
+}
+
+# Wait for queued kernel/udev events
+wait_for_udev()
+{
+ command -v udevadm >/dev/null 2>&1 || return 0
+ udevadm settle ${1:+--timeout=$1}
+}
+
+# Find a specific fstab entry
+# $1=mountpoint
+# $2=fstype (optional)
+# returns 0 on success, 1 on failure (not found or no fstab)
+read_fstab_entry() {
+ # Not found by default.
+ found=1
+
+ for file in ${rootmnt?}/etc/fstab; do
+ if [ -f "$file" ]; then
+ # shellcheck disable=SC2034
+ while read -r MNT_FSNAME MNT_DIR MNT_TYPE MNT_OPTS MNT_FREQ MNT_PASS MNT_JUNK; do
+ case "$MNT_FSNAME" in
+ ""|\#*)
+ continue;
+ ;;
+ esac
+ if [ "$MNT_DIR" = "$1" ]; then
+ if [ -n "${2-}" ]; then
+ [ "$MNT_TYPE" = "$2" ] || continue;
+ fi
+ found=0
+ break 2
+ fi
+ done < "$file"
+ fi
+ done
+
+ return $found
+}
+
+# Resolve device node from a name. This expands any LABEL or UUID.
+# $1=name
+# Resolved name is echoed.
+resolve_device() {
+ DEV="$1"
+
+ case "$DEV" in
+ LABEL=* | UUID=* | PARTLABEL=* | PARTUUID=*)
+ DEV="$(blkid -l -t "$DEV" -o device)" || return 1
+ ;;
+ esac
+ [ -e "$DEV" ] && echo "$DEV"
+}
+
+# Check a file system.
+# $1=device
+# $2=mountpoint (for diagnostics only)
+# $3=type (may be "auto")
+_checkfs_once()
+{
+ DEV="$1"
+ NAME="$2"
+ TYPE="$3"
+ if [ "$NAME" = "/" ] ; then
+ NAME="root"
+ fi
+ FSCK_LOGFILE=/run/initramfs/fsck.log
+ FSCK_STAMPFILE=/run/initramfs/fsck-${NAME#/}
+
+ if [ "${TYPE}" = "auto" ]; then
+ TYPE="$(get_fstype "${DEV}")"
+ fi
+
+ FSCKCODE=0
+ if [ -z "${TYPE}" ]; then
+ log_warning_msg "Type of $NAME file system is unknown, so skipping check."
+ return
+ fi
+ if ! command -v fsck >/dev/null 2>&1; then
+ log_warning_msg "fsck not present, so skipping $NAME file system"
+ return
+ fi
+ if [ "${fastboot?}" = "y" ] ; then
+ log_warning_msg "Fast boot enabled, so skipping $NAME file system check."
+ return
+ fi
+
+ if [ "${forcefsck?}" = "y" ]
+ then
+ force="-f"
+ else
+ force=""
+ fi
+
+ if [ "${fsckfix?}" = "y" ]
+ then
+ fix="-y"
+ elif [ "${fsckfix?}" = "n" ]
+ then
+ fix="-n"
+ else
+ fix="-a"
+ fi
+
+ spinner=""
+ if [ -z "${debug?}" ]; then
+ spinner="-C"
+ fi
+
+ if [ "${quiet}" = n ]
+ then
+ log_begin_msg "Will now check $NAME file system"
+ # shellcheck disable=SC2086
+ logsave -a -s $FSCK_LOGFILE fsck $spinner $force $fix -V -t "$TYPE" "$DEV"
+ FSCKCODE=$?
+ log_end_msg
+ else
+ log_begin_msg "Checking $NAME file system"
+ # shellcheck disable=SC2086
+ logsave -a -s $FSCK_LOGFILE fsck $spinner $force $fix -T -t "$TYPE" "$DEV"
+ FSCKCODE=$?
+ log_end_msg
+ fi
+
+ # NOTE: "failure" is defined as exiting with a return code of
+ # 4, possibly or-ed with other flags. A return code of 1
+ # indicates that file system errors were corrected but that
+ # the boot may proceed.
+ #
+ if [ "$FSCKCODE" -eq 32 ]
+ then
+ log_warning_msg "File system check was interrupted by user"
+ elif [ $((FSCKCODE & 4)) -eq 4 ]
+ then
+ log_failure_msg "File system check of the $NAME filesystem failed"
+ return 1
+ elif [ "$FSCKCODE" -gt 1 ]
+ then
+ log_warning_msg "File system check failed but did not detect errors"
+ sleep 5
+ else
+ true >"$FSCK_STAMPFILE"
+ fi
+ return 0
+}
+
+checkfs()
+{
+ while ! _checkfs_once "$@"; do
+ panic "The $2 filesystem on $1 requires a manual fsck"
+ done
+}
+
+# Mount a file system. We parse the information from the fstab. This
+# should be overridden by any boot script which can mount arbitrary
+# filesystems such as /usr. This default implementation delegates to
+# local or nfs based upon the filesystem type.
+# $1=mountpoint mount location
+mountfs()
+{
+ type=local
+ read_fstab_entry "$1"
+ if [ "${MNT_TYPE}" = "nfs" ] || [ "${MNT_TYPE}" = "nfs4" ]; then
+ type=nfs
+ fi
+
+ ${type}_mount_fs "$1"
+}
+
+# Mount the root file system. It should be overridden by all
+# boot scripts.
+mountroot()
+{
+ :
+}
+
+# Run /scripts/${boot}-top. This should be overridden by all boot
+# scripts.
+mount_top()
+{
+ :
+}
+
+# Run /scripts/${boot}-premount. This should be overridden by all boot
+# scripts.
+mount_premount()
+{
+ :
+}
+
+# Run /scripts/${boot}-bottom. This should be overridden by all boot
+# scripts.
+mount_bottom()
+{
+ :
+}