#!/bin/bash
#
# /etc/rc: runlevel-changing script
#

# Functions to print error messages and status reports
printerror() {
  printf "[ERROR] %s" "$1"
}
printconf() {
  printf "%s: %s\n" "$1" "$2"
}
printinfo() {
  printf "%s\n" "$1"
}

main() {
  # Load configuration
  . /etc/rc.conf

  # Load colors if the user enables them and the appropriate file exists
  if [ "$USE_COLOR" = true ] && [ -f /etc/rc.colors ]; then
    . /etc/rc.colors
  fi

  # printinfo and printconf become no-ops if $VERBOSE is not true
  if [ "$VERBOSE" != true ]; then
    printconf() {
      true
    }
    printinfo() {
      true
    }
  fi

  called_as="${0##*/}"
  case "${called_as#*.}" in
    multi) multi ;;
    single) single ;;
    shutdown) shutdown ;;
    rc|sysinit) sysinit ;;
    *) printerror "Usage: rc.[sysinit|multi|single|shutdown]\n" ;;
  esac
}

##
# End preamble. Now for the individual runlevels ...
##

sysinit() {
  printf "%s\n" "The system is coming up. Please wait."

  # Setup a working environment with eudev
  # - if /dev is not mounted - mount as a devtmpfs (CONFIG_DEVTMPFS=y)
  # - if /dev is mounted (e.g. due to handover from initramfs or
  #   CONFIG_DEVTMPFS_MOUNT=y), remount with specific options
  # - some video drivers require exec access in /dev, thus it's set here
  # - for completeness, we add few sanity limits (2k non-empty files, 16k inodes)

  printinfo "Mounting /proc.."
  /bin/mountpoint -q /proc || /bin/mount -t proc none /proc || { printerror "\n"; exit 1;}

  printinfo "Mounting /sys.."
  /bin/mountpoint -q /sys || /bin/mount -t sysfs none /sys || { printerror "\n"; exit 1; }

  printinfo "Mounting /run.."
  RUNOPTS="mode=0755,nosuid,nodev,exec" 
  /bin/mountpoint -q /run || /bin/mount -n -t tmpfs -o ${RUNOPTS} tmpfs /run \
    || { printerror "\n"; exit 1; }

  printinfo "Mounting /dev.."
  UDEVOPTS="exec,nosuid,noatime,mode=0755,nr_blocks=2048,nr_inodes=16384"
  if ! /bin/mountpoint -q /dev; then
    /bin/mount -n -t devtmpfs -o ${UDEVOPTS} dev /dev || { printerror "\n"; exit 1; }
  else
    /bin/mount -n -o remount,${UDEVOPTS} dev /dev || { printerror "\n"; exit 1; }
  fi

  printinfo "Mounting /dev/pts.."
  if ! /bin/mountpoint -q /dev/pts; then
      /bin/mkdir -m 755 /dev/pts
      /bin/mount -t devpts -o noexec,nosuid,gid=tty,mode=0620 devpts /dev/pts || \
      { printerror "\n" ; exit 1; }
  fi

  printinfo "Mounting /dev/shm.."
  if ! /bin/mountpoint -q /dev/shm; then
      /bin/mkdir -m 1777 /dev/shm
      /bin/mount shm -t tmpfs -o defaults,exec,rw /dev/shm || { printerror "\n"; exit 1; }
  fi

  printinfo "Starting udev.."
  /sbin/start_udev || { printerror "\n"; exit 1; }

  # Create device-mapper device nodes and scan for LVM volume groups
  if [ -x /sbin/lvm ] && /sbin/vgscan --mknodes --ignorelockingfailure &>/dev/null \
     && [[ "$(/sbin/lvm vgdisplay 2>/dev/null)" == *"VG Name"* ]]; then
      printinfo "LVM volume groups detected. Activating..."
      /sbin/vgchange --sysinit -a y || printerror "\n"
  fi

  # Mount root read-only
  while IFS=' ' read -r _ mountpoint _ options; do
    if [ "$mountpoint" = "/" ]; then
      case "$options" in
        *ro*) mounted_ro=1 ;;
        *)    mounted_ro="" ;;
      esac
      break
    fi
  done < /proc/mounts
  if [ ! "$mounted_ro" ]; then
    printinfo "Mounting / in read-only mode.."
    /bin/mount -o remount,ro / || { printerror ; exit 1; }
  fi

  printinfo "Checking filesystems.."
  if [ -f /forcefsck ]; then
    printinfo "/forcefsck found, passing -f flag to fsck.."
    /sbin/fsck -A -T -C -a -f
  else
    /sbin/fsck -A -T -C -a
  fi

  if [ $? -gt 1 ]; then
    cat << EOF
***************  FILESYSTEM CHECK FAILED  ******************
*                                                          *
*  Please repair manually and reboot. Note that the root   *
*  file system is currently mounted read-only. To remount  *
*  it read-write type: mount -n -o remount,rw /            *
*  When you exit the maintenance shell the system will     *
*  reboot automatically.                                   *
*                                                          *
************************************************************
EOF
    /sbin/sulogin -p
    printinfo "Automatic reboot in progress..."
    /bin/umount -a -r
    /bin/mount -o remount,ro /
    /sbin/reboot -f
    exit 0
  fi

  # Mount local filesystems
  printinfo "Mounting / in read-write mode.."
  /bin/mount -o remount,rw / || printerror "\n"

  # Run module initialization script
  if [ -x /etc/rc.modules ]; then
    printinfo "Loading kernel modules.."
    /etc/rc.modules >/dev/null 2>&1 &
  fi

  printinfo "Mounting other filesystems (excl. network devices).."
  /bin/mount -a -O no_netdev || printerror "\n"

  printinfo "Activating swap.."
  /sbin/swapon -a || printerror "\n"

  # Create user dir in /run
  printinfo "Creating /run/user.."
  /bin/mkdir -m 0755 /run/user || printerror "\n"

  # Clean up misc files
  printinfo "Deleting temporary files.."
  : > /run/utmp || printerror "\n"
  /bin/rm -rf /forcefsck /fastboot /etc/nologin /etc/shutdownpid || printerror "\n"
  (cd /var/lock && /usr/bin/find . ! -type d -delete) || printerror "\n"
  (cd /tmp && /usr/bin/find . ! -name . -delete) || printerror "\n"
  /bin/mkdir -m 1777 /tmp/.ICE-unix /tmp/.X11-unix || printerror "\n"

  # Set kernel variables
  printinfo "Setting kernel variables.."
  /sbin/sysctl -e --system > /dev/null || printerror "\n"

  # Update shared library links
  printinfo "Updating shared library links.."
  /sbin/ldconfig

  # Configure host name
  if [ "$HOSTNAME" ]; then
    printconf "Hostname" "$HOSTNAME"
    /bin/hostname "$HOSTNAME"
  fi

  # Kernels newer than 5.6 can generate enough entropy on their own
  # According to random(4) the seed file needs to be "saved across reboots"
  /bin/cat /var/lib/urandom/seed > /dev/urandom

  # Configure system clock
  if [ "$TIMEZONE" ]; then
    /bin/ln -snf /usr/share/zoneinfo/"$TIMEZONE" /etc/localtime
  fi
  /sbin/hwclock --hctosys

  # Default language
  printconf "LANG" "${LANG:=C.UTF-8}"
  export LANG

  # Load console font
  if [ "$FONT" ]; then
    printconf "Font" "$FONT"
    /usr/bin/setfont "$FONT"
  fi

  # Load console keymap
  if [ "$KEYMAP" ]; then
    printconf "Keyboard" "$KEYMAP"
    /usr/bin/loadkeys -q "$KEYMAP"
  fi

  # Screen timeout, defaults to 15m
  printconf "Screen Timeout" "${BLANKTIME:=15}"
  /usr/bin/setterm -blank "${BLANKTIME}"

  # Save boot messages
  /bin/dmesg > /var/log/boot

  # End of sysinit
}

multi() {
  # multi-user runlevel
  printf "%s\n" "Entering multi-user mode..."

  # Run fixes startup file
  if [ -x /etc/rc.fix ]; then
    /etc/rc.fix
  fi

  # Start services
  if [ "${SYSLOG}${SERVICES[*]}" ]; then
    printf "%s:" "Starting services"
    if [ -f /etc/rc.d/$SYSLOG ] && [ -x /etc/rc.d/$SYSLOG ]; then
      printf " %s" "$SYSLOG"
      /etc/rc.d/$SYSLOG start &> /dev/null || printerror
    fi
    for service in "${SERVICES[@]}"; do
      printf " %s" "$service"
      /etc/rc.d/"$service" start &> /tmp/rc.$$ || printerror
      /usr/bin/logger -t "$service" -f /tmp/rc.$$
      /bin/rm -f /tmp/rc.$$
    done
    printf '\n'
  fi

  # Run local startup script
  if [ -x /etc/rc.local ]; then
    /etc/rc.local
  fi

  # End of multi
}

single() {
  # single-user runlevel
  printf "%s\n" "Entering single-user mode..."

  if [ "$PREVLEVEL" = "2" ]; then
    # Shutdown services
    if [ "${SERVICES[*]}" ]; then
      s_i=${#SERVICES[@]}
      while (( s_i > 0 )); do
        s_i=$(( s_i-1 ))
        /etc/rc.d/"${SERVICES[$s_i]}" start &> /tmp/rc.$$
        /usr/bin/logger -t "${SERVICES[$s_i]}" -f /tmp/rc.$$
        /bin/rm -f /tmp/rc.$$
      done
    fi
  fi

  if [ "$PREVLEVEL" != "N" ]; then
    # Terminate all processes
    /sbin/killall5 -15
    /bin/sleep 5
    /sbin/killall5 -9

    /bin/mountpoint -q /proc || /bin/mount -n -t proc none /proc
    /bin/mountpoint -q /sys || /bin/mount -n -t sysfs none /sys
    /sbin/start_udev

    if [ -f /etc/rc.d/"$SYSLOG" ] && [ -x /etc/rc.d/"$SYSLOG" ]; then
        /etc/rc.d/"$SYSLOG" start &> /dev/null
    fi    
  fi

  if [ "$RUNLEVEL" = "1" ]; then
    # Enter single-user mode
    exec /sbin/init -t1 S
  fi

  # End of single
}

shutdown() {
  # Set linefeed mode to avoid staircase effect
  /bin/stty onlcr

  printf "\n%s\n" "The system is coming down. Please wait."

  # Run any customized shutdown commands
  if [ -x /etc/rc.shutdown.local ]; then
    /etc/rc.shutdown.local
  fi

  if [ "$PREVLEVEL" = "2" ]; then
    # Shutdown services
    printf "%s:" "Stopping services"
    if [ "${SERVICES[*]}" ]; then
      s_i=${#SERVICES[@]}
      while (( s_i > 0 )); do
        s_i=$(( s_i-1 ))
        printf " %s" "${SERVICES[$s_i]}"
        /etc/rc.d/"${SERVICES[$s_i]}" stop &> /tmp/rc.$$ || printerror
        /usr/bin/logger -t "${SERVICES[$s_i]}" -f /tmp/rc.$$
        /bin/rm -f /tmp/rc.$$
      done
    fi
    printf '\n'
  fi

  # Terminate all processes
  /sbin/killall5 -15
  /bin/sleep 5
  /sbin/killall5 -9

  # Random seed manipulations not needed for kernels newer than 5.6
  # according to random(4) the seed file needs to be "saved across reboots"
  /bin/dd if=/dev/urandom of=/var/lib/urandom/seed count=1 2>/dev/null

  # Save system clock
  /sbin/hwclock --systohc

  # Turn off swap
  /sbin/swapoff -a

  # Write to wtmp file before unmounting
  /sbin/halt -w

  # Unmount all file systems except sysfs, proc, tmpfs, and devtmpfs
  sync; sync
  /bin/umount -a -d -r -t nosysfs,noproc,notmpfs,nodevtmpfs

  # Check if LVM is installed and LVM volume groups are defined
  if [ -x /sbin/lvm ] && [[ "$(/sbin/lvm vgdisplay 2>/dev/null)" == *"VG Name"* ]]; then
    printinfo "LVM volume groups found. Deactivating..."
    if /sbin/vgchange --ignorelockingfailure -a n; then
      printinfo "LVM volume groups deactivated."
    else
      printinfo "LVM deactivation failed. Continuing anyway."
    fi
  fi

  # Unmount all remaining filesystems. On failure, try to remount read-only.
  /bin/umount -a -r

  sync
  printinfo "Remounting root filesystem read-only..."
  /bin/mount -o remount,ro / || printerror "\n"

  # Power off or reboot
  if [ "$RUNLEVEL" = "0" ]; then
    /sbin/poweroff -d -f -i
  else
    /sbin/reboot -d -f -i
  fi

  # End of shutdown sequence
}

main
