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

Generated by cgit