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 ${@}
|