summaryrefslogtreecommitdiff
path: root/init
blob: 0c50ec4af7dc9fa8d067b7de9b6d8473147f220a (plain)
    1 #!/bin/bash
    2 
    3 export DEBUG=0
    4 export QUIET=0
    5 export INTERACTIVE=0
    6 export ROOTDEV=''
    7 export CRYPTROOT=''
    8 
    9 screen_init() {
   10   # Clear screen
   11   [ "${QUIET}" -eq 1 ] && clear
   12 
   13   # Output message file if it exists
   14   [ -f /etc/msg ] && cat /etc/msg
   15 }
   16 
   17 
   18 #
   19 # Mounts the fakeroot from the kernel "root" param
   20 #
   21 mount_fakeroot() {
   22   local rootdev=${1}
   23   local fakeroot=${2}
   24 
   25   mount -o ro ${rootdev} ${fakeroot}
   26 }
   27 
   28 
   29 log() {
   30   local lvl="${1}"
   31   local msg="${2}"
   32   [ "${QUIET}" -eq 1 ] && [ "${lvl,,}" = "info"  ] && return
   33 
   34   printf -- "%s\n" "${msg}"
   35   if [ "${DEBUG}" -ne 0 ]; then
   36     echo "Press enter to continue"
   37     read
   38   fi
   39 }
   40  
   41  
   42 #
   43 # Resolves the path to the root device from the cmdline root= syntax.
   44 # Currently handles UUID syntax and /dev/sdx syntax
   45 #
   46 parse_cmdline() {
   47   local cmdline=${1}
   48 
   49   for i in ${cmdline[@]}; do
   50     case "${i}" in
   51       root=*)
   52         if [ "${i:5:4}" == 'UUID' ]; then
   53           # mount by uuid
   54           local uuid=$(echo ${i} | cut -d '=' -f 3)
   55           ROOTDEV="$(blkid -U ${uuid})"
   56         elif [ "${i:5:5}" == 'LABEL' ]; then
   57           # mount by label
   58           local label=$(echo ${i} | cut -d '=' -f 3)
   59           ROOTDEV="$(blkid -L ${label})"
   60         else
   61           # mount by dev
   62           ROOTDEV="$(echo ${i} | cut -d '=' -f 2)"
   63         fi
   64       ;;
   65       cryptroot=*)
   66         if [ "${i:10:4}" == 'UUID' ]; then
   67           # mount by uuid
   68           local uuid=$(echo ${i} | cut -d '=' -f 3)
   69           CRYPTROOT="$(blkid -U ${uuid})"
   70         elif [ "${i:10:5}" == 'LABEL' ]; then
   71           # mount by label
   72           local label=$(echo ${i} | cut -d '=' -f 3)
   73           CRYPTROOT="$(blkid -L ${label})"
   74         else
   75           # mount by dev
   76           CRYPTROOT="$(echo ${i} | cut -d '=' -f 2)"
   77         fi
   78       ;;
   79       initdebug)
   80         # Enable debug mode (this is gonna be slow)
   81         DEBUG=1
   82       ;;
   83       interactive)
   84         # Enable interactive mode
   85         INTERACTIVE=1
   86       ;;
   87       quiet)
   88         #  Enable quiet boot (less output)
   89         QUIET=1
   90       ;;
   91     esac
   92   done
   93 }
   94 
   95 
   96 #
   97 # Takes an encrypted device path and executes cryptsetup luksOpen. Returns path
   98 # to the new decrypted block device. This path takes the rough form of
   99 # /dev/mapper/_dev_sdx
  100 #
  101 setup_encrypted() {
  102   local path="${1}"
  103   local name="$(echo ${path} | tr / _)"
  104 
  105   # Decrypted block dev path is /dev/mapper/${name}
  106   cryptsetup luksOpen ${path} ${name}
  107 
  108   # Notify user and wait for input on failure
  109   if [ "$?" -gt 0 ]; then
  110     # Ensure decryption was successful, it not, drop to debug shell and reboot once done.
  111     printf 'ERROR: Could not decrypt device\n' >&2
  112     printf 'Dropping to debug shell (will automatically reboot on exit).\n' >&2
  113     /bin/bash --norc -i
  114     printf 'Rebooting\n' >&2
  115     printf 'b' > /proc/sysrq-trigger
  116   fi
  117 
  118   # Probe the new crypto device for an additional partition table
  119   partprobe "/dev/mapper/${name}" 2>/dev/null 1>/dev/null
  120 
  121   # Success. Return the path of the decrypted root device
  122   echo "/dev/mapper/${name}"
  123 }
  124 
  125 
  126 #
  127 # Waits up to a threshold (in seconds) for the specified path to appear as a
  128 # block device. A check for device presence occurs once per second.
  129 #
  130 # If the device appears within the threshold time, the function returns early
  131 # with error code 0 (all is well - no need to wait more).
  132 #
  133 # If the block device fails to appear within the threshold time, the function
  134 # returns code 2 (not found)
  135 #
  136 # @param dev     Path to the device to wait for
  137 # @param timeout Maximum time (in seconds) to wait for the device to appear
  138 #
  139 wait_dev() {
  140   local dev=${1}
  141   local timeout=${2}
  142 
  143   local wait=0
  144 
  145   while [ ${wait} -lt ${timeout} ]; do
  146     # If the path exists as a block device, exit the wait loop
  147     [ -b "${dev}" ] && return 0
  148     # Increment the wait counter
  149     wait=$(( wait + 1 ))
  150     # Wait another second for the device to appear
  151     sleep 1
  152   done
  153 
  154   return 2
  155 }
  156 
  157 
  158 #
  159 # Main function to keep the main operations code nicely separated from the
  160 # rest.
  161 #
  162 main() {
  163   # Mount the /proc and /sys filesystems.
  164   mount -t tmpfs                        none  /run
  165   mount -t devtmpfs                     none  /dev
  166   mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
  167   mount -t proc  -o nodev,noexec,nosuid proc  /proc
  168 
  169   local fakeroot='/mnt/root'
  170 
  171   if [ ! -d "${fakeroot}" ]; then
  172     log INFO "Fake root location ${fakeroot} does not exist. Creating."
  173     mkdir ${fakeroot}
  174   fi
  175 
  176   parse_cmdline "$(cat /proc/cmdline)"
  177 
  178   # display fanciful boot image
  179   screen_init
  180 
  181   log INFO "Root device: ${ROOTDEV}"
  182 
  183   log INFO "Waiting up to 10 seconds for root device to appear."
  184   wait_dev "${ROOTDEV}" 10
  185 
  186   # Drop to maintenance shell if root device does not exist.
  187   if [ "$?" -ne 0 ]; then
  188     log ERROR "ERROR: Root device ${ROOTDEV} does not exist or can not be accessed."
  189     log ERROR "       Dropping to maintenance shell for troubleshooting."
  190     log ERROR "       (You may just need to wait longer for the root device)"
  191     /bin/bash -i
  192   fi
  193 
  194   if cryptsetup isLuks "${ROOTDEV}"; then
  195     # Set new rootdev location (/dev/mapper/something). This will update it to
  196     # the decrypted block device path.
  197     log INFO "Root device ${ROOTDEV} is encrypted."
  198     ROOTDEV=$(setup_encrypted ${ROOTDEV})
  199     log INFO "New rootdev: ${ROOTDEV}"
  200   fi
  201 
  202   if [ ! -z "${CRYPTROOT}" ]; then
  203     log INFO "Cryptroot defined. Changing rootdev to '${CRYPTROOT}'"
  204     ROOTDEV="${CRYPTROOT}"
  205   fi
  206 
  207   # Drop to interactive shell if requested
  208   if [ "${INTERACTIVE}" == 1 ]; then
  209     log INFO "Interractive shell requested. Type 'exit' to continue boot sequence."
  210     /bin/bash --norc
  211   fi
  212 
  213   # Mount the fakeroot.
  214   log INFO "Mounting fakeroot"
  215   mount_fakeroot ${ROOTDEV} ${fakeroot}
  216 
  217   # Ensure switch_root will be possible for destination fakeroot
  218   if [ ! -f "${fakeroot}/sbin/init" ]; then
  219     log ERROR "ERROR: Destination fakeroot ${fakeroot} does not have an init script."
  220     log ERROR "       Cannot proceed. Dropping to shell for troubleshooting."
  221     /bin/bash -i
  222   fi
  223 
  224   # Boot the real McCoy
  225   log INFO "Switching root"
  226   exec switch_root ${fakeroot} /sbin/init
  227 
  228   # This will be reached if previous command failed
  229   log ERROR "There was an error performing switch_root to ${fakeroot}."
  230   log ERROR "Starting recovery shell."
  231   /bin/bash -i
  232 }
  233 
  234 main ${@}

Generated by cgit