blob: 8ea174abde9a687e58bb6de79e6fb81686b85ade (
plain)
1 #!/bin/bash
2
3 export DEBUG=0
4 export INTERACTIVE=0
5 export ROOTDEV=''
6
7 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 mount_fakeroot() {
20 local rootdev=${1}
21 local fakeroot=${2}
22
23 mount -o ro ${rootdev} ${fakeroot}
24 }
25
26
27 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 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 get_part_type() {
82 local path="${1}"
83 local 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 setup_encrypted() {
98 local path="${1}"
99 local 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 # Probe the new crypto device for an additional partition table
112 partprobe "/dev/mapper/${name}" 2>/dev/null 1>/dev/null
113
114 # Success. Return the path of the decrypted root device
115 echo "/dev/mapper/${name}"
116 }
117
118
119 #
120 # Waits up to a threshold (in seconds) for the specified path to appear as a
121 # block device. A check for device presence occurs once per second.
122 #
123 # If the device appears within the threshold time, the function returns early
124 # with error code 0 (all is well - no need to wait more).
125 #
126 # If the block device fails to appear within the threshold time, the function
127 # returns code 2 (not found)
128 #
129 # @param dev Path to the device to wait for
130 # @param timeout Maximum time (in seconds) to wait for the device to appear
131 #
132 wait_dev() {
133 local dev=${1}
134 local timeout=${2}
135
136 local wait=0
137
138 while [ ${wait} -lt ${timeout} ]; do
139 # If the path exists as a block device, exit the wait loop
140 [ -b "${dev}" ] && return 0
141 # Increment the wait counter
142 wait=$(( wait + 1 ))
143 # Wait another second for the device to appear
144 sleep 1
145 done
146
147 return 2
148 }
149
150
151 #
152 # Main function to keep the main operations code nicely separated from the
153 # rest.
154 #
155 main() {
156 # display fanciful boot image
157 screen_init
158
159 # Mount the /proc and /sys filesystems.
160 mount -t devtmpfs none /dev
161 mount -t proc none /proc
162 mount -t tmpfs none /run
163 mount -t sysfs none /sys
164
165 local fakeroot='/mnt/root'
166
167 if [ ! -d "${fakeroot}" ]; then
168 log "Fake root location ${fakeroot} does not exist. Creating."
169 mkdir ${fakeroot}
170 fi
171
172 log "Parsing cmdline arguments"
173
174 parse_cmdline "$(cat /proc/cmdline)"
175
176 log "Root device: ${ROOTDEV}"
177
178 log "Waiting up to 10 seconds for root device to appear."
179 wait_dev "${ROOTDEV}" 10
180
181 # Drop to maintenance shell if root device does not exist.
182 if [ "$?" -ne 0 ]; then
183 log "ERROR: Root device ${ROOTDEV} does not exist or can not be accessed."
184 log " Dropping to maintenance shell for troubleshooting."
185 log " (You may just need to wait longer for the root device)"
186 /bin/bash -i
187 fi
188
189 if [ "$(get_part_type ${ROOTDEV})" == 'luks' ]; then
190 # Set new rootdev location (/dev/mapper/something). This will update it to
191 # the decrypted block device path.
192 log "Root device ${ROOTDEV} is encrypted."
193 ROOTDEV=$(setup_encrypted ${ROOTDEV})
194 log "New rootdev: ${ROOTDEV}"
195 fi
196
197 # Drop to interactive shell if requested
198 if [ "${INTERACTIVE}" == 1 ]; then
199 log "Interractive shell requested. Type 'exit' to continue boot sequence."
200 /bin/bash --norc
201 fi
202
203 # Mount the fakeroot.
204 log "Mounting fakeroot"
205 mount_fakeroot ${ROOTDEV} ${fakeroot}
206
207 # Ensure switch_root will be possible for destination fakeroot
208 if [ ! -f "${fakeroot}/sbin/init" ]; then
209 log "ERROR: Destination fakeroot ${fakeroot} does not have an init script."
210 log " Cannot proceed. Dropping to shell for troubleshooting."
211 /bin/bash -i
212 fi
213
214 # Boot the real McCoy
215 log "Switching root"
216 exec switch_root ${fakeroot} /sbin/init
217
218 # This will be reached if previous command failed
219 log "There was an error performing switch_root to ${fakeroot}."
220 log "Starting recovery shell."
221 /bin/bash -i
222 }
223
224 main ${@}
|