blob: 66b0ddca4cf6dd2aaa1204a0a2c23d7e5d2af16f (
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 devtmpfs none /dev
160 mount -t proc none /proc
161 mount -t tmpfs none /run
162 mount -t sysfs none /sys
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 ${@}
|