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

Generated by cgit