summaryrefslogtreecommitdiff
path: root/init
blob: f5d6eecdd8c433d14201a482f8c8a390e626caf8 (plain)
    1 #!/bin/bash
    2 # Init script for mkinitramfs
    3 # Copyright (C)  2023  Aaron Ball <nullspoon@oper.io>
    4 #
    5 # This program is free software: you can redistribute it and/or modify
    6 # it under the terms of the GNU General Public License as published by
    7 # the Free Software Foundation, either version 3 of the License, or
    8 # (at your option) any later version.
    9 #
   10 # This program is distributed in the hope that it will be useful,
   11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
   12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13 # GNU General Public License for more details.
   14 #
   15 # You should have received a copy of the GNU General Public License
   16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
   17 
   18 export DEBUG=0
   19 export QUIET=0
   20 export INTERACTIVE=0
   21 export ROOTDEV=''
   22 export MOUNTOPTS=()
   23 
   24 screen_init() {
   25   # Clear screen
   26   [ "${QUIET}" -eq 1 ] && printf '\033c'
   27 
   28   # Output message file if it exists
   29   [ -f /etc/msg ] && printf '%s\n' "$(</etc/msg)"
   30 }
   31 
   32 
   33 log() {
   34   local lvl="${1}"
   35   local msg="${2}"
   36   [ "${QUIET}" -eq 1 ] && [ "${lvl,,}" = "info"  ] && return
   37 
   38   printf -- "%s\n" "${msg}"
   39   if [ "${DEBUG}" -ne 0 ]; then
   40     echo "Press enter to continue"
   41     read
   42   fi
   43 }
   44  
   45  
   46 #
   47 # Resolves the path to the root device from the cmdline root= syntax.
   48 # Currently handles UUID syntax and /dev/sdx syntax, btrfs subvols, debugging,
   49 # and quiet options.
   50 #
   51 parse_cmdline() {
   52   local cmdline="$(<${1:?})"
   53 
   54   for i in ${cmdline[@]}; do
   55     case "${i}" in
   56       root=*)
   57         if [ "${i:5:4}" == 'UUID' ]; then
   58           # mount by uuid
   59           ROOTDEV="/dev/disk/by-uuid/${i##*=}"
   60         elif [ "${i:5:5}" == 'LABEL' ]; then
   61           # mount by label
   62           ROOTDEV=/dev/disk/by-partlabel/${i##*=}
   63         else
   64           # mount by dev
   65           ROOTDEV="${i##*=}"
   66         fi
   67       ;;
   68       subvol=*)
   69         MOUNTOPTS+=(-o subvol=${i#*=})
   70       ;;
   71       initdebug)
   72         # Enable debug mode (this is gonna be slow)
   73         DEBUG=1
   74       ;;
   75       interactive)
   76         # Enable interactive mode
   77         INTERACTIVE=1
   78       ;;
   79       quiet)
   80         #  Enable quiet boot (less output)
   81         QUIET=1
   82       ;;
   83     esac
   84   done
   85 }
   86 
   87 
   88 #
   89 # Takes an encrypted device path and executes cryptsetup luksOpen. Returns path
   90 # to the new decrypted block device. This path takes the rough form of
   91 # /dev/mapper/_dev_sdx
   92 #
   93 setup_encrypted() {
   94   local path="${1}"
   95   local name="${path//\//_}"
   96 
   97   # Decrypted block dev path is /dev/mapper/${name}
   98   cryptsetup luksOpen ${path} ${name}
   99 
  100   # Notify user and wait for input on failure
  101   if [ "$?" -gt 0 ]; then
  102     # Ensure decryption was successful, it not, drop to debug shell and reboot once done.
  103     printf 'ERROR: Could not decrypt device\n' >&2
  104     printf 'Dropping to debug shell (will automatically reboot on exit).\n' >&2
  105     /bin/bash --norc -i
  106     printf 'Rebooting\n' >&2
  107     printf 'b' > /proc/sysrq-trigger
  108   fi
  109 
  110   # Probe the new crypto device for an additional partition table
  111   partprobe "/dev/mapper/${name}" 2>/dev/null 1>/dev/null
  112 
  113   # Success. Return the path of the decrypted root device
  114   echo "/dev/mapper/${name}"
  115 }
  116 
  117 
  118 udev_populate() {
  119   # Start udevd if not already running
  120   pidof -s udevd > /dev/null || /sbin/udevd --daemon
  121 
  122   /sbin/udevadm trigger --type=devices --action=add --subsystem-match=block
  123   /sbin/udevadm trigger --type=devices --action=add --subsystem-match=input
  124   /sbin/udevadm settle
  125 
  126   # Stop udevd once devices are populated so main udevd can finish booting
  127   kill $(pidof udevd)
  128 }
  129 
  130 
  131 #
  132 # Main function to keep the main operations code nicely separated from the
  133 # rest.
  134 #
  135 main() {
  136   # Mount the /proc and /sys filesystems.
  137   mount -t tmpfs -o mode=0755,nousid,nodev none   /run
  138   mount -t sysfs -o nodev,noexec,nosuid    sysfs  /sys
  139   mount -t proc  -o nodev,noexec,nosuid    proc   /proc
  140   mount -t tmpfs                           shm    /dev/shm
  141   mount -t devtmpfs -o exec,nosuid,noatime dev    /dev
  142   mount -t devpts                          devpts /dev/pts
  143 
  144   local fakeroot='/mnt/root'
  145 
  146   if [ ! -d "${fakeroot}" ]; then
  147     log INFO "Fake root location ${fakeroot} does not exist. Cannot boot."
  148   fi
  149 
  150   parse_cmdline /proc/cmdline
  151 
  152   # display fanciful boot image
  153   screen_init
  154   udev_populate
  155 
  156   log INFO "Root device: ${ROOTDEV}"
  157 
  158   # Drop to maintenance shell if root device does not exist.
  159   if [ "$?" -ne 0 ]; then
  160     log ERROR "ERROR: Root device ${ROOTDEV} does not exist or can not be accessed."
  161     log ERROR "       Dropping to maintenance shell for troubleshooting."
  162     log ERROR "       (You may just need to wait longer for the root device)"
  163     /bin/bash -i
  164   fi
  165 
  166   # Modprobe atkbd to ensure user can type password
  167   for i in i8042 atkbd; do
  168     printf 'Loading required module [%s]\n' "${i}"
  169     modprobe "${i}" || printf 'Failed loading %s\n' "${i}"
  170   done
  171 
  172   if cryptsetup isLuks "${ROOTDEV}"; then
  173     # Set new rootdev location (/dev/mapper/something). This will update it to
  174     # the decrypted block device path.
  175     log INFO "Root device ${ROOTDEV} is encrypted."
  176     ROOTDEV=$(setup_encrypted ${ROOTDEV})
  177     log INFO "New rootdev: ${ROOTDEV}"
  178   fi
  179 
  180   # Drop to interactive shell if requested
  181   if [ "${INTERACTIVE}" -eq 1 ]; then
  182     log INFO "Interractive shell requested. Type 'exit' to continue boot sequence."
  183     /bin/bash --norc
  184   fi
  185 
  186   # Mount the fakeroot.
  187   log INFO "Mounting fakeroot"
  188   mount -o ro ${MOUNTOPTS[@]} ${ROOTDEV} ${fakeroot}
  189 
  190   # Ensure switch_root will be possible for destination fakeroot
  191   if [ ! -f "${fakeroot}/sbin/init" ]; then
  192     log ERROR "ERROR: Destination fakeroot ${fakeroot} does not have an init script."
  193     log ERROR "       Cannot proceed. Dropping to shell for troubleshooting."
  194     /bin/bash -i
  195   fi
  196 
  197   # Boot the real McCoy
  198   log INFO "Switching root"
  199   exec switch_root ${fakeroot} /sbin/init
  200 
  201   # This will be reached if previous command failed
  202   log ERROR "There was an error performing switch_root to ${fakeroot}."
  203   log ERROR "Starting recovery shell."
  204   /bin/bash -i
  205 }
  206 
  207 main ${@}

Generated by cgit