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

Generated by cgit