]> code.delx.au - refind/blob - install.sh
0.6.11 release.
[refind] / install.sh
1 #!/bin/bash
2 #
3 # Linux/MacOS X script to install rEFInd
4 #
5 # Usage:
6 #
7 # ./install.sh [options]
8 #
9 # options include:
10 # "--esp" to install to the ESP rather than to the system's root
11 # filesystem. This is the default on Linux
12 # "--usedefault {devicefile}" to install as default
13 # (/EFI/BOOT/BOOTX64.EFI and similar) to the specified device
14 # (/dev/sdd1 or whatever) without registering with the NVRAM.
15 # "--root {dir}" to specify installation using the specified directory
16 # as the system's root
17 # "--alldrivers" to install all drivers along with regular files
18 # "--nodrivers" to suppress driver installation (default in Linux is
19 # driver used on /boot; --nodrivers is OS X default)
20 # "--shim {shimfile}" to install a shim.efi file for Secure Boot
21 # "--localkeys" to re-sign x86-64 binaries with a locally-generated key
22 # "--yes" to assume a "yes" response to all prompts
23 #
24 # The "esp" option is valid only on Mac OS X; it causes
25 # installation to the EFI System Partition (ESP) rather than
26 # to the current OS X boot partition. Under Linux, this script
27 # installs to the ESP by default.
28 #
29 # This program is copyright (c) 2012 by Roderick W. Smith
30 # It is released under the terms of the GNU GPL, version 3,
31 # a copy of which should be included in the file COPYING.txt.
32 #
33 # Revision history:
34 #
35 # 0.6.11 -- Improvements in script's ability to handle directories with spaces
36 # in their names
37 # 0.6.9 -- Install gptsync on Macs
38 # 0.6.8 -- Bug fix: ESP scan now uses "uniq".
39 # 0.6.6 -- Bug fix: Upgrade drivers when installed to EFI/BOOT. Also enable
40 # copying shim.efi and MokManager.efi over themselves.
41 # 0.6.4 -- Copies ext2 driver rather than ext4 driver for ext2/3fs
42 # 0.6.3 -- Support for detecting rEFInd in EFI/BOOT and EFI/Microsoft/Boot
43 # directories & for installing to EFI/BOOT in BIOS mode
44 # 0.6.2-1 -- Added --yes option & tweaked key-copying for use with RPM install script
45 # 0.6.1 -- Added --root option; minor bug fixes
46 # 0.6.0 -- Changed --drivers to --alldrivers and added --nodrivers option;
47 # changed default driver installation behavior in Linux to install
48 # the driver needed to read /boot (if available)
49 # 0.5.1.2 -- Fixed bug that caused failure to generate refind_linux.conf file
50 # 0.5.1.1 -- Fixed bug that caused script failure under OS X
51 # 0.5.1 -- Added --shim & --localkeys options & create sample refind_linux.conf
52 # in /boot
53 # 0.5.0 -- Added --usedefault & --drivers options & changed "esp" option to "--esp"
54 # 0.4.5 -- Fixed check for rEFItBlesser in OS X
55 # 0.4.2 -- Added notice about BIOS-based OSes & made NVRAM changes in Linux smarter
56 # 0.4.1 -- Added check for rEFItBlesser in OS X
57 # 0.3.3.1 -- Fixed OS X 10.7 bug; also works as make target
58 # 0.3.2.1 -- Check for presence of source files; aborts if not present
59 # 0.3.2 -- Initial version
60 #
61 # Note: install.sh version numbers match those of the rEFInd package
62 # with which they first appeared.
63
64 RootDir="/"
65 TargetDir=/EFI/refind
66 LocalKeysBase="refind_local"
67 ShimSource="none"
68 TargetShim="default"
69 TargetX64="refind_x64.efi"
70 TargetIA32="refind_ia32.efi"
71 LocalKeys=0
72 DeleteRefindDir=0
73 AlwaysYes=0
74
75 #
76 # Functions used by both OS X and Linux....
77 #
78
79 GetParams() {
80 InstallToEspOnMac=0
81 if [[ $OSName == "Linux" ]] ; then
82 # Install the driver required to read /boot, if it's available
83 InstallDrivers="boot"
84 else
85 InstallDrivers="none"
86 fi
87 while [[ $# -gt 0 ]]; do
88 case $1 in
89 --esp | --ESP) InstallToEspOnMac=1
90 ;;
91 --usedefault) TargetDir=/EFI/BOOT
92 TargetPart="$2"
93 TargetX64="bootx64.efi"
94 TargetIA32="bootia32.efi"
95 shift
96 ;;
97 --root) RootDir="$2"
98 shift
99 ;;
100 --localkeys) LocalKeys=1
101 ;;
102 --shim) ShimSource="$2"
103 shift
104 ;;
105 --drivers | --alldrivers) InstallDrivers="all"
106 ;;
107 --nodrivers) InstallDrivers="none"
108 ;;
109 --yes) AlwaysYes=1
110 ;;
111 * ) echo "Usage: $0 [--esp | --usedefault {device-file} | --root {directory} ]"
112 echo " [--nodrivers | --alldrivers] [--shim {shim-filename}]"
113 echo " [--localkeys] [--yes]"
114 exit 1
115 esac
116 shift
117 done
118
119 if [[ $InstallToEspOnMac == 1 && "$TargetDir" == '/EFI/BOOT' ]] ; then
120 echo "You may use --esp OR --usedefault, but not both! Aborting!"
121 exit 1
122 fi
123 if [[ "$RootDir" != '/' && "$TargetDir" == '/EFI/BOOT' ]] ; then
124 echo "You may use --usedefault OR --root, but not both! Aborting!"
125 exit 1
126 fi
127 if [[ "$RootDir" != '/' && $InstallToEspOnMac == 1 ]] ; then
128 echo "You may use --root OR --esp, but not both! Aborting!"
129 exit 1
130 fi
131
132 RLConfFile="$RootDir/boot/refind_linux.conf"
133 EtcKeysDir="$RootDir/etc/refind.d/keys"
134 } # GetParams()
135
136 # Get a yes/no response from the user and place it in the YesNo variable.
137 # If the AlwaysYes variable is set to 1, skip the user input and set "Y"
138 # in the YesNo variable.
139 ReadYesNo() {
140 if [[ $AlwaysYes == 1 ]] ; then
141 YesNo="Y"
142 echo "Y"
143 else
144 read YesNo
145 fi
146 }
147
148 # Abort if the rEFInd files can't be found.
149 # Also sets $ConfFile to point to the configuration file,
150 # $IconsDir to point to the icons directory, and
151 # $ShimSource to the source of the shim.efi file (if necessary).
152 CheckForFiles() {
153 # Note: This check is satisfied if EITHER the 32- or the 64-bit version
154 # is found, even on the wrong platform. This is because the platform
155 # hasn't yet been determined. This could obviously be improved, but it
156 # would mean restructuring lots more code....
157 if [[ ! -f "$RefindDir/refind_ia32.efi" && ! -f "$RefindDir/refind_x64.efi" ]] ; then
158 echo "The rEFInd binary file is missing! Aborting installation!"
159 exit 1
160 fi
161
162 if [[ -f "$RefindDir/refind.conf-sample" ]] ; then
163 ConfFile="$RefindDir/refind.conf-sample"
164 elif [[ -f "$ThisDir/refind.conf-sample" ]] ; then
165 ConfFile="$ThisDir/refind.conf-sample"
166 else
167 echo "The sample configuration file is missing! Aborting installation!"
168 exit 1
169 fi
170
171 if [[ -d "$RefindDir/icons" ]] ; then
172 IconsDir="$RefindDir/icons"
173 elif [[ -d "$ThisDir/icons" ]] ; then
174 IconsDir="$ThisDir/icons"
175 else
176 echo "The icons directory is missing! Aborting installation!"
177 exit 1
178 fi
179
180 if [[ "$ShimSource" != "none" ]] ; then
181 if [[ -f "$ShimSource" ]] ; then
182 TargetX64="grubx64.efi"
183 MokManagerSource=`dirname "$ShimSource"`/MokManager.efi
184 else
185 echo "The specified shim file, $ShimSource, doesn't exist!"
186 echo "Aborting installation!"
187 exit 1
188 fi
189 fi
190 } # CheckForFiles()
191
192 # Helper for CopyRefindFiles; copies shim files (including MokManager, if it's
193 # available) to target.
194 CopyShimFiles() {
195 cp -fb "$ShimSource" "$InstallDir/$TargetDir/$TargetShim"
196 if [[ $? != 0 ]] ; then
197 Problems=1
198 fi
199 if [[ -f "$MokManagerSource" ]] ; then
200 cp -fb "$MokManagerSource" "$InstallDir/$TargetDir/"
201 fi
202 if [[ $? != 0 ]] ; then
203 Problems=1
204 fi
205 } # CopyShimFiles()
206
207 # Copy the public keys to the installation medium
208 CopyKeys() {
209 if [[ $LocalKeys == 1 ]] ; then
210 mkdir -p "$InstallDir/$TargetDir/keys/"
211 cp "$EtcKeysDir/$LocalKeysBase.cer" "$InstallDir/$TargetDir/keys/"
212 cp "$EtcKeysDir/$LocalKeysBase.crt" "$InstallDir/$TargetDir/keys/"
213 fi
214 } # CopyKeys()
215
216 # Copy drivers from $RefindDir/drivers_$1 to $InstallDir/$TargetDir/drivers_$1,
217 # honoring the $InstallDrivers condition. Must be passed a suitable
218 # architecture code (ia32 or x64).
219 CopyDrivers() {
220 if [[ $InstallDrivers == "all" ]] ; then
221 mkdir -p "$InstallDir/$TargetDir/drivers_$1"
222 cp "$ThisDir"/drivers_$1/*_$1.efi "$InstallDir/$TargetDir/drivers_$1/" 2> /dev/null
223 cp "$RefindDir"/drivers_$1/*_$1.efi "$InstallDir/$TargetDir/drivers_$1/" 2> /dev/null
224 elif [[ "$InstallDrivers" == "boot" && -x `which blkid` ]] ; then
225 BootPart=`df /boot | grep dev | cut -f 1 -d " "`
226 BootFS=`blkid -o export $BootPart 2> /dev/null | grep TYPE= | cut -f 2 -d =`
227 DriverType=""
228 case $BootFS in
229 ext2 | ext3) DriverType="ext2"
230 # Could use ext4, but that can create unwanted entries from symbolic
231 # links in / to /boot/vmlinuz if a separate /boot partition is used.
232 ;;
233 ext4) DriverType="ext4"
234 ;;
235 reiserfs) DriverType="reiserfs"
236 ;;
237 hfsplus) DriverType="hfs"
238 ;;
239 *) BootFS=""
240 esac
241 if [[ -n $BootFS ]] ; then
242 echo "Installing driver for $BootFS (${DriverType}_$1.efi)"
243 mkdir -p "$InstallDir/$TargetDir/drivers_$1"
244 cp "$ThisDir/drivers_$1/${DriverType}_$1.efi" "$InstallDir/$TargetDir/drivers_$1/" 2> /dev/null
245 cp "$RefindDir/drivers_$1/${DriverType}_$1.efi" "$InstallDir/$TargetDir/drivers_$1"/ 2> /dev/null
246 fi
247 fi
248 }
249
250 # Copy tools (currently only gptsync, and that only on Macs) to the EFI/tools
251 # directory on the ESP. Must be passed a suitable architecture code (ia32
252 # or x64).
253 CopyTools() {
254 mkdir -p $InstallDir/EFI/tools
255 if [[ $OSName == 'Darwin' ]] ; then
256 cp -f "$RefindDir/tools_$1/gptsync_$1.efi" "$InstallDir/EFI/tools/"
257 if [[ -f "$InstallDir/EFI/tools/gptsync.efi" ]] ; then
258 mv "$InstallDir/EFI/tools/gptsync.efi" "$InstallDir/EFI/tools/gptsync.efi-disabled"
259 echo "Found old gptsync.efi; disabling it by renaming it to gptsync.efi-disabled"
260 fi
261 fi
262 } # CopyTools()
263
264 # Copy the rEFInd files to the ESP or OS X root partition.
265 # Sets Problems=1 if any critical commands fail.
266 CopyRefindFiles() {
267 mkdir -p "$InstallDir/$TargetDir"
268 if [[ "$TargetDir" == '/EFI/BOOT' ]] ; then
269 cp "$RefindDir/refind_ia32.efi" "$InstallDir/$TargetDir/$TargetIA32" 2> /dev/null
270 if [[ $? != 0 ]] ; then
271 echo "Note: IA32 (x86) binary not installed!"
272 fi
273 cp "$RefindDir/refind_x64.efi" "$InstallDir/$TargetDir/$TargetX64" 2> /dev/null
274 if [[ $? != 0 ]] ; then
275 Problems=1
276 fi
277 if [[ "$ShimSource" != "none" ]] ; then
278 TargetShim="bootx64.efi"
279 CopyShimFiles
280 fi
281 if [[ $InstallDrivers == "all" ]] ; then
282 cp -r "$RefindDir"/drivers_* "$InstallDir/$TargetDir/" 2> /dev/null
283 cp -r "$ThisDir"/drivers_* "$InstallDir/$TargetDir/" 2> /dev/null
284 elif [[ $Upgrade == 1 ]] ; then
285 if [[ $Platform == 'EFI64' ]] ; then
286 CopyDrivers x64
287 CopyTools x64
288 else
289 CopyDrivers ia32
290 CopyTools ia32
291 fi
292 fi
293 Refind=""
294 CopyKeys
295 elif [[ $Platform == 'EFI64' || $TargetDir == "/EFI/Microsoft/Boot" ]] ; then
296 cp "$RefindDir/refind_x64.efi" "$InstallDir/$TargetDir/$TargetX64"
297 if [[ $? != 0 ]] ; then
298 Problems=1
299 fi
300 CopyDrivers x64
301 CopyTools x64
302 Refind="refind_x64.efi"
303 CopyKeys
304 if [[ "$ShimSource" != "none" ]] ; then
305 if [[ "$TargetShim" == "default" ]] ; then
306 TargetShim=`basename "$ShimSource"`
307 fi
308 CopyShimFiles
309 Refind="$TargetShim"
310 if [[ $LocalKeys == 0 ]] ; then
311 echo "Storing copies of rEFInd Secure Boot public keys in $EtcKeysDir"
312 mkdir -p "$EtcKeysDir"
313 cp "$ThisDir/keys/refind.cer" "$EtcKeysDir" 2> /dev/null
314 cp "$ThisDir/keys/refind.crt" "$EtcKeysDir" 2> /dev/null
315 fi
316 fi
317 elif [[ $Platform == 'EFI32' ]] ; then
318 cp "$RefindDir/refind_ia32.efi" "$InstallDir/$TargetDir/$TargetIA32"
319 if [[ $? != 0 ]] ; then
320 Problems=1
321 fi
322 CopyDrivers ia32
323 CopyTools ia32
324 Refind="refind_ia32.efi"
325 else
326 echo "Unknown platform! Aborting!"
327 exit 1
328 fi
329 echo "Copied rEFInd binary files"
330 echo ""
331 if [[ -d "$InstallDir/$TargetDir/icons" ]] ; then
332 rm -rf "$InstallDir/$TargetDir/icons-backup" &> /dev/null
333 mv -f "$InstallDir/$TargetDir/icons" "$InstallDir/$TargetDir/icons-backup"
334 echo "Notice: Backed up existing icons directory as icons-backup."
335 fi
336 cp -r "$IconsDir" "$InstallDir/$TargetDir"
337 if [[ $? != 0 ]] ; then
338 Problems=1
339 fi
340 mkdir -p "$InstallDir/$TargetDir/keys"
341 cp -rf "$ThisDir"/keys/*.[cd]er "$InstallDir/$TargetDir/keys/" 2> /dev/null
342 cp -rf "$EtcKeysDir"/*.[cd]er "$InstallDir/$TargetDir/keys/" 2> /dev/null
343 if [[ -f "$InstallDir/$TargetDir/refind.conf" ]] ; then
344 echo "Existing refind.conf file found; copying sample file as refind.conf-sample"
345 echo "to avoid overwriting your customizations."
346 echo ""
347 cp -f "$ConfFile" "$InstallDir/$TargetDir"
348 if [[ $? != 0 ]] ; then
349 Problems=1
350 fi
351 else
352 echo "Copying sample configuration file as refind.conf; edit this file to configure"
353 echo "rEFInd."
354 echo ""
355 cp -f "$ConfFile" "$InstallDir/$TargetDir/refind.conf"
356 if [[ $? != 0 ]] ; then
357 Problems=1
358 fi
359 fi
360 if [[ $DeleteRefindDir == 1 ]] ; then
361 echo "Deleting the temporary directory $RefindDir"
362 rm -r "$RefindDir"
363 fi
364 } # CopyRefindFiles()
365
366 # Mount the partition the user specified with the --usedefault option
367 MountDefaultTarget() {
368 InstallDir=/tmp/refind_install
369 mkdir -p "$InstallDir"
370 if [[ $OSName == 'Darwin' ]] ; then
371 mount -t msdos "$TargetPart" "$InstallDir"
372 elif [[ $OSName == 'Linux' ]] ; then
373 mount -t vfat "$TargetPart" "$InstallDir"
374 fi
375 if [[ $? != 0 ]] ; then
376 echo "Couldn't mount $TargetPart ! Aborting!"
377 rmdir "$InstallDir"
378 exit 1
379 fi
380 UnmountEsp=1
381 } # MountDefaultTarget()
382
383 #
384 # A series of OS X support functions....
385 #
386
387 # Mount the ESP at /Volumes/ESP or determine its current mount
388 # point.
389 # Sets InstallDir to the ESP mount point
390 # Sets UnmountEsp if we mounted it
391 MountOSXESP() {
392 # Identify the ESP. Note: This returns the FIRST ESP found;
393 # if the system has multiple disks, this could be wrong!
394 Temp=`diskutil list | grep " EFI "`
395 Esp=/dev/`echo $Temp | cut -f 5 -d ' '`
396 # If the ESP is mounted, use its current mount point....
397 Temp=`df | grep "$Esp"`
398 InstallDir=`echo $Temp | cut -f 6 -d ' '`
399 if [[ "$InstallDir" == '' ]] ; then
400 mkdir /Volumes/ESP &> /dev/null
401 mount -t msdos "$Esp" /Volumes/ESP
402 if [[ $? != 0 ]] ; then
403 echo "Unable to mount ESP! Aborting!\n"
404 exit 1
405 fi
406 UnmountEsp=1
407 InstallDir="/Volumes/ESP"
408 fi
409 } # MountOSXESP()
410
411 # Control the OS X installation.
412 # Sets Problems=1 if problems found during the installation.
413 InstallOnOSX() {
414 echo "Installing rEFInd on OS X...."
415 if [[ "$TargetDir" == "/EFI/BOOT" ]] ; then
416 MountDefaultTarget
417 elif [[ "$InstallToEspOnMac" == "1" ]] ; then
418 MountOSXESP
419 else
420 InstallDir="$RootDir/"
421 fi
422 echo "Installing rEFInd to the partition mounted at $InstallDir"
423 Platform=`ioreg -l -p IODeviceTree | grep firmware-abi | cut -d "\"" -f 4`
424 CopyRefindFiles
425 if [[ $InstallToEspOnMac == "1" ]] ; then
426 bless --mount "$InstallDir" --setBoot --file "$InstallDir/$TargetDir/$Refind"
427 elif [[ "$TargetDir" != "/EFI/BOOT" ]] ; then
428 bless --setBoot --folder "$InstallDir/$TargetDir" --file "$InstallDir/$TargetDir/$Refind"
429 fi
430 if [[ $? != 0 ]] ; then
431 Problems=1
432 fi
433 if [[ -f /Library/StartupItems/rEFItBlesser || -d /Library/StartupItems/rEFItBlesser ]] ; then
434 echo
435 echo "/Library/StartupItems/rEFItBlesser found!"
436 echo "This program is part of rEFIt, and will cause rEFInd to fail to work after"
437 echo -n "its first boot. Do you want to remove rEFItBlesser (Y/N)? "
438 ReadYesNo
439 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
440 echo "Deleting /Library/StartupItems/rEFItBlesser..."
441 rm -r /Library/StartupItems/rEFItBlesser
442 else
443 echo "Not deleting rEFItBlesser."
444 fi
445 fi
446 echo
447 echo "WARNING: If you have an Advanced Format disk, *DO NOT* attempt to check the"
448 echo "bless status with 'bless --info', since this is known to cause disk corruption"
449 echo "on some systems!!"
450 echo
451 } # InstallOnOSX()
452
453
454 #
455 # Now a series of Linux support functions....
456 #
457
458 # Check for evidence that we're running in Secure Boot mode. If so, and if
459 # appropriate options haven't been set, warn the user and offer to abort.
460 # If we're NOT in Secure Boot mode but the user HAS specified the --shim
461 # or --localkeys option, warn the user and offer to abort.
462 #
463 # FIXME: Although I checked the presence (and lack thereof) of the
464 # /sys/firmware/efi/vars/SecureBoot* files on my Secure Boot test system
465 # before releasing this script, I've since found that they are at least
466 # sometimes present when Secure Boot is absent. This means that the first
467 # test can produce false alarms. A better test is highly desirable.
468 CheckSecureBoot() {
469 VarFile=`ls -d /sys/firmware/efi/vars/SecureBoot* 2> /dev/null`
470 if [[ -n "$VarFile" && "$TargetDir" != '/EFI/BOOT' && "$ShimSource" == "none" ]] ; then
471 echo ""
472 echo "CAUTION: Your computer appears to support Secure Boot, but you haven't"
473 echo "specified a valid shim.efi file source. If you've disabled Secure Boot and"
474 echo "intend to leave it disabled, this is fine; but if Secure Boot is active, the"
475 echo "resulting installation won't boot. You can read more about this topic at"
476 echo "http://www.rodsbooks.com/refind/secureboot.html."
477 echo ""
478 echo -n "Do you want to proceed with installation (Y/N)? "
479 ReadYesNo
480 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
481 echo "OK; continuing with the installation..."
482 else
483 exit 0
484 fi
485 fi
486
487 if [[ "$ShimSource" != "none" && ! -n "$VarFile" ]] ; then
488 echo ""
489 echo "You've specified installing using a shim.efi file, but your computer does not"
490 echo "appear to be running in Secure Boot mode. Although installing in this way"
491 echo "should work, it's unnecessarily complex. You may continue, but unless you"
492 echo "plan to enable Secure Boot, you should consider stopping and omitting the"
493 echo "--shim option. You can read more about this topic at"
494 echo "http://www.rodsbooks.com/refind/secureboot.html."
495 echo ""
496 echo -n "Do you want to proceed with installation (Y/N)? "
497 ReadYesNo
498 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
499 echo "OK; continuing with the installation..."
500 else
501 exit 0
502 fi
503 fi
504
505 if [[ $LocalKeys != 0 && ! -n "$VarFile" ]] ; then
506 echo ""
507 echo "You've specified re-signing your rEFInd binaries with locally-generated keys,"
508 echo "but your computer does not appear to be running in Secure Boot mode. The"
509 echo "keys you generate will be useless unless you enable Secure Boot. You may"
510 echo "proceed with this installation, but before you do so, you may want to read"
511 echo "more about it at http://www.rodsbooks.com/refind/secureboot.html."
512 echo ""
513 echo -n "Do you want to proceed with installation (Y/N)? "
514 ReadYesNo
515 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
516 echo "OK; continuing with the installation..."
517 else
518 exit 0
519 fi
520 fi
521
522 } # CheckSecureBoot()
523
524 # Check for the presence of locally-generated keys from a previous installation in
525 # $EtcKeysDir (/etc/refind.d/keys). If they're not present, generate them using
526 # openssl.
527 GenerateKeys() {
528 PrivateKey="$EtcKeysDir/$LocalKeysBase.key"
529 CertKey="$EtcKeysDir/$LocalKeysBase.crt"
530 DerKey="$EtcKeysDir/$LocalKeysBase.cer"
531 OpenSSL=`which openssl 2> /dev/null`
532
533 # Do the work only if one or more of the necessary keys is missing
534 # TODO: Technically, we don't need the DerKey; but if it's missing and openssl
535 # is also missing, this will fail. This could be improved.
536 if [[ ! -f "$PrivateKey" || ! -f "$CertKey" || ! -f "$DerKey" ]] ; then
537 echo "Generating a fresh set of local keys...."
538 mkdir -p "$EtcKeysDir"
539 chmod 0700 "$EtcKeysDir"
540 if [[ ! -x "$OpenSSL" ]] ; then
541 echo "Can't find openssl, which is required to create your private signing keys!"
542 echo "Aborting!"
543 exit 1
544 fi
545 if [[ -f "$PrivateKey" ]] ; then
546 echo "Backing up existing $PrivateKey"
547 cp -f "$PrivateKey" "$PrivateKey.backup" 2> /dev/null
548 fi
549 if [[ -f "$CertKey" ]] ; then
550 echo "Backing up existing $CertKey"
551 cp -f "$CertKey" "$CertKey.backup" 2> /dev/null
552 fi
553 if [[ -f "$DerKey" ]] ; then
554 echo "Backing up existing $DerKey"
555 cp -f "$DerKey" "$DerKey.backup" 2> /dev/null
556 fi
557 "$OpenSSL" req -new -x509 -newkey rsa:2048 -keyout "$PrivateKey" -out "$CertKey" \
558 -nodes -days 3650 -subj "/CN=Locally-generated rEFInd key/"
559 "$OpenSSL" x509 -in "$CertKey" -out "$DerKey" -outform DER
560 chmod 0600 "$PrivateKey"
561 else
562 echo "Using existing local keys...."
563 fi
564 }
565
566 # Sign a single binary. Requires parameters:
567 # $1 = source file
568 # $2 = destination file
569 # Also assumes that the SBSign, PESign, UseSBSign, UsePESign, and various key variables are set
570 # appropriately.
571 # Aborts script on error
572 SignOneBinary() {
573 $SBSign --key "$PrivateKey" --cert "$CertKey" --output "$2" "$1"
574 if [[ $? != 0 ]] ; then
575 echo "Problem signing the binary $1! Aborting!"
576 exit 1
577 fi
578 }
579
580 # Re-sign the x86-64 binaries with a locally-generated key, First look for appropriate
581 # key files in $EtcKeysDir. If they're present, use them to re-sign the binaries. If
582 # not, try to generate new keys and store them in $EtcKeysDir.
583 ReSignBinaries() {
584 SBSign=`which sbsign 2> /dev/null`
585 echo "Found sbsign at $SBSign"
586 TempDir="/tmp/refind_local"
587 if [[ ! -x "$SBSign" ]] ; then
588 echo "Can't find sbsign, which is required to sign rEFInd with your own keys!"
589 echo "Aborting!"
590 exit 1
591 fi
592 GenerateKeys
593 mkdir -p "$TempDir/drivers_x64"
594 cp "$RefindDir/refind.conf-sample $TempDir" 2> /dev/null
595 cp "$ThisDir/refind.conf-sample $TempDir" 2> /dev/null
596 cp "$RefindDir/refind_ia32.efi $TempDir" 2> /dev/null
597 cp -a "$RefindDir/drivers_ia32 $TempDir" 2> /dev/null
598 cp -a "$ThisDir/drivers_ia32 $TempDir" 2> /dev/null
599 SignOneBinary "$RefindDir/refind_x64.efi" "$TempDir/refind_x64.efi"
600 SaveIFS=$IFS
601 IFS=$(echo -en "\n\b")
602 for Driver in `ls "$RefindDir"/drivers_x64/*.efi "$ThisDir"/drivers_x64/*.efi 2> /dev/null` ; do
603 TempName=`basename "$Driver"`
604 SignOneBinary "$Driver" "$TempDir/drivers_x64/$TempName"
605 done
606 IFS=$SaveIFS
607 RefindDir="$TempDir"
608 DeleteRefindDir=1
609 }
610
611 # Identifies the ESP's location (/boot or /boot/efi, or these locations under
612 # the directory specified by --root); aborts if the ESP isn't mounted at
613 # either location.
614 # Sets InstallDir to the ESP mount point.
615 FindLinuxESP() {
616 EspLine=`df "$RootDir/boot/efi" 2> /dev/null | grep boot/efi`
617 if [[ ! -n "$EspLine" ]] ; then
618 EspLine=`df "$RootDir"/boot | grep boot`
619 fi
620 InstallDir=`echo $EspLine | cut -d " " -f 6`
621 if [[ -n "$InstallDir" ]] ; then
622 EspFilesystem=`grep "$InstallDir" /etc/mtab | uniq | cut -d " " -f 3`
623 fi
624 echo "EspFilesystem is '$EspFilesystem'"
625 if [[ $EspFilesystem != 'vfat' ]] ; then
626 echo "$RootDir/boot/efi doesn't seem to be on a VFAT filesystem. The ESP must be"
627 echo "mounted at $RootDir/boot or $RootDir/boot/efi and it must be VFAT! Aborting!"
628 exit 1
629 fi
630 echo "ESP was found at $InstallDir using $EspFilesystem"
631 } # FindLinuxESP
632
633 # Uses efibootmgr to add an entry for rEFInd to the EFI's NVRAM.
634 # If this fails, sets Problems=1
635 AddBootEntry() {
636 InstallIt="0"
637 Efibootmgr=`which efibootmgr 2> /dev/null`
638 if [[ "$Efibootmgr" ]] ; then
639 InstallDisk=`grep "$InstallDir" /etc/mtab | cut -d " " -f 1 | cut -c 1-8`
640 PartNum=`grep "$InstallDir" /etc/mtab | cut -d " " -f 1 | cut -c 9-10`
641 EntryFilename="$TargetDir/$Refind"
642 EfiEntryFilename=`echo ${EntryFilename//\//\\\}`
643 EfiEntryFilename2=`echo ${EfiEntryFilename} | sed s/\\\\\\\\/\\\\\\\\\\\\\\\\/g`
644 ExistingEntry=`"$Efibootmgr" -v | grep -i "$EfiEntryFilename2"`
645
646 if [[ "$ExistingEntry" ]] ; then
647 ExistingEntryBootNum=`echo "$ExistingEntry" | cut -c 5-8`
648 FirstBoot=`"$Efibootmgr" | grep BootOrder | cut -c 12-15`
649 if [[ "$ExistingEntryBootNum" != "$FirstBoot" ]] ; then
650 echo "An existing rEFInd boot entry exists, but isn't set as the default boot"
651 echo "manager. The boot order is being adjusted to make rEFInd the default boot"
652 echo "manager. If this is NOT what you want, you should use efibootmgr to"
653 echo "manually adjust your EFI's boot order."
654 "$Efibootmgr" -b $ExistingEntryBootNum -B &> /dev/null
655 InstallIt="1"
656 fi
657 else
658 InstallIt="1"
659 fi
660
661 if [[ $InstallIt == "1" ]] ; then
662 echo "Installing it!"
663 "$Efibootmgr" -c -l "$EfiEntryFilename" -L "rEFInd Boot Manager" -d $InstallDisk -p $PartNum &> /dev/null
664 if [[ $? != 0 ]] ; then
665 EfibootmgrProblems=1
666 Problems=1
667 fi
668 fi
669
670 else # efibootmgr not found
671 EfibootmgrProblems=1
672 Problems=1
673 fi
674
675 if [[ $EfibootmgrProblems ]] ; then
676 echo
677 echo "ALERT: There were problems running the efibootmgr program! You may need to"
678 echo "rename the $Refind binary to the default name (EFI/boot/bootx64.efi"
679 echo "on x86-64 systems or EFI/boot/bootia32.efi on x86 systems) to have it run!"
680 echo
681 fi
682 } # AddBootEntry()
683
684 # Create a minimal/sample refind_linux.conf file in /boot.
685 GenerateRefindLinuxConf() {
686 if [[ -f "$RLConfFile" ]] ; then
687 echo "Existing $RLConfFile found; not overwriting."
688 else
689 if [[ -f "$RootDir/etc/default/grub" ]] ; then
690 # We want the default options used by the distribution, stored here....
691 source "$RootDir/etc/default/grub"
692 fi
693 RootFS=`df "$RootDir" | grep dev | cut -f 1 -d " "`
694 StartOfDevname=`echo "$RootFS" | cut -b 1-7`
695 if [[ "$StartOfDevname" == "/dev/sd" || "$StartOfDevName" == "/dev/hd" ]] ; then
696 # Identify root filesystem by UUID rather than by device node, if possible
697 Uuid=`blkid -o export "$RootFS" 2> /dev/null | grep UUID=`
698 if [[ -n $Uuid ]] ; then
699 RootFS="$Uuid"
700 fi
701 fi
702 DefaultOptions="$GRUB_CMDLINE_LINUX $GRUB_CMDLINE_LINUX_DEFAULT"
703 echo "\"Boot with standard options\" \"ro root=$RootFS $DefaultOptions \"" > $RLConfFile
704 echo "\"Boot to single-user mode\" \"ro root=$RootFS $DefaultOptions single\"" >> $RLConfFile
705 echo "\"Boot with minimal options\" \"ro root=$RootFS\"" >> $RLConfFile
706 fi
707 }
708
709 # Set varaibles for installation in EFI/BOOT directory
710 SetVarsForBoot() {
711 TargetDir="/EFI/BOOT"
712 if [[ $ShimSource == "none" ]] ; then
713 TargetX64="bootx64.efi"
714 TargetIA32="bootia32.efi"
715 else
716 TargetX64="grubx64.efi"
717 TargetIA32="bootia32.efi"
718 TargetShim="bootx64.efi"
719 fi
720 } # SetFilenamesForBoot()
721
722 # Set variables for installation in EFI/Microsoft/Boot directory
723 SetVarsForMsBoot() {
724 TargetDir="/EFI/Microsoft/Boot"
725 if [[ $ShimSource == "none" ]] ; then
726 TargetX64="bootmgfw.efi"
727 else
728 TargetX64="grubx64.efi"
729 TargetShim="bootmgfw.efi"
730 fi
731 }
732
733 # TargetDir defaults to /EFI/refind; however, this function adjusts it as follows:
734 # - If an existing refind.conf is available in /EFI/BOOT or /EFI/Microsoft/Boot,
735 # install to that directory under the suitable name; but DO NOT do this if
736 # refind.conf is also in /EFI/refind.
737 # - If booted in BIOS mode and the ESP lacks any other EFI files, install to
738 # /EFI/BOOT
739 # - If booted in BIOS mode and there's no refind.conf file and there is a
740 # /EFI/Microsoft/Boot/bootmgfw.efi file, move it down one level and
741 # install under that name, "hijacking" the Windows boot loader filename
742 DetermineTargetDir() {
743 Upgrade=0
744
745 if [[ -f $InstallDir/EFI/BOOT/refind.conf ]] ; then
746 SetVarsForBoot
747 Upgrade=1
748 fi
749 if [[ -f $InstallDir/EFI/Microsoft/Boot/refind.conf ]] ; then
750 SetVarsForMsBoot
751 Upgrade=1
752 fi
753 if [[ -f $InstallDir/EFI/refind/refind.conf ]] ; then
754 TargetDir="/EFI/refind"
755 Upgrade=1
756 fi
757 if [[ $Upgrade == 1 ]] ; then
758 echo "Found rEFInd installation in $InstallDir$TargetDir; upgrading it."
759 fi
760
761 if [[ ! -d /sys/firmware/efi && $Upgrade == 0 ]] ; then # BIOS-mode
762 FoundEfiFiles=`find "$InstallDir/EFI/BOOT" -name "*.efi" 2> /dev/null`
763 FoundConfFiles=`find "$InstallDir" -name "refind\.conf" 2> /dev/null`
764 if [[ ! -n "$FoundConfFiles" && -f "$InstallDir/EFI/Microsoft/Boot/bootmgfw.efi" ]] ; then
765 mv -n "$InstallDir/EFI/Microsoft/Boot/bootmgfw.efi" "$InstallDir/EFI/Microsoft" &> /dev/null
766 SetVarsForMsBoot
767 echo "Running in BIOS mode with a suspected Windows installation; moving boot loader"
768 echo "files so as to install to $InstallDir$TargetDir."
769 elif [[ ! -n "$FoundEfiFiles" ]] ; then # In BIOS mode and no default loader; install as default loader
770 SetVarsForBoot
771 echo "Running in BIOS mode with no existing default boot loader; installing to"
772 echo $InstallDir$TargetDir
773 else
774 echo "Running in BIOS mode with an existing default boot loader; backing it up and"
775 echo "installing rEFInd in its place."
776 if [[ -d "$InstallDir/EFI/BOOT-rEFIndBackup" ]] ; then
777 echo ""
778 echo "Caution: An existing backup of a default boot loader exists! If the current"
779 echo "default boot loader and the backup are different boot loaders, the current"
780 echo "one will become inaccessible."
781 echo ""
782 echo -n "Do you want to proceed with installation (Y/N)? "
783 ReadYesNo
784 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
785 echo "OK; continuing with the installation..."
786 else
787 exit 0
788 fi
789 fi
790 mv -n "$InstallDir/EFI/BOOT" "$InstallDir/EFI/BOOT-rEFIndBackup"
791 SetVarsForBoot
792 fi
793 fi # BIOS-mode
794 } # DetermineTargetDir()
795
796 # Controls rEFInd installation under Linux.
797 # Sets Problems=1 if something goes wrong.
798 InstallOnLinux() {
799 echo "Installing rEFInd on Linux...."
800 modprobe efivars &> /dev/null
801 if [[ $TargetDir == "/EFI/BOOT" ]] ; then
802 MountDefaultTarget
803 else
804 FindLinuxESP
805 DetermineTargetDir
806 fi
807 CpuType=`uname -m`
808 if [[ $CpuType == 'x86_64' ]] ; then
809 Platform="EFI64"
810 elif [[ ($CpuType == 'i386' || $CpuType == 'i486' || $CpuType == 'i586' || $CpuType == 'i686') ]] ; then
811 Platform="EFI32"
812 # If we're in EFI mode, do some sanity checks, and alert the user or even
813 # abort. Not in BIOS mode, though, since that could be used on an emergency
814 # disc to try to recover a troubled Linux installation.
815 if [[ -d /sys/firmware/efi ]] ; then
816 if [[ "$ShimSource" != "none" && "$TargetDir" != "/BOOT/EFI" ]] ; then
817 echo ""
818 echo "CAUTION: shim does not currently supports 32-bit systems, so you should not"
819 echo "use the --shim option to install on such systems. Aborting!"
820 echo ""
821 exit 1
822 fi
823 echo
824 echo "CAUTION: This Linux installation uses a 32-bit kernel. 32-bit EFI-based"
825 echo "computers are VERY RARE. If you've installed a 32-bit version of Linux"
826 echo "on a 64-bit computer, you should manually install the 64-bit version of"
827 echo "rEFInd. If you're installing on a Mac, you should do so from OS X. If"
828 echo "you're positive you want to continue with this installation, answer 'Y'"
829 echo "to the following question..."
830 echo
831 echo -n "Are you sure you want to continue (Y/N)? "
832 ReadYesNo
833 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
834 echo "OK; continuing with the installation..."
835 else
836 exit 0
837 fi
838 fi # in EFI mode
839 else
840 echo "Unknown CPU type '$CpuType'; aborting!"
841 exit 1
842 fi
843
844 if [[ $LocalKeys == 1 ]] ; then
845 ReSignBinaries
846 fi
847
848 CheckSecureBoot
849 CopyRefindFiles
850 if [[ "$TargetDir" != "/EFI/BOOT" && "$TargetDir" != "/EFI/Microsoft/Boot" ]] ; then
851 AddBootEntry
852 GenerateRefindLinuxConf
853 fi
854 } # InstallOnLinux()
855
856 #
857 # The main part of the script. Sets a few environment variables,
858 # performs a few startup checks, and then calls functions to
859 # install under OS X or Linux, depending on the detected platform.
860 #
861
862 OSName=`uname -s`
863 GetParams "$@"
864 ThisDir="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
865 RefindDir="$ThisDir/refind"
866 ThisScript="$ThisDir/`basename $0`"
867 if [[ `whoami` != "root" ]] ; then
868 echo "Not running as root; attempting to elevate privileges via sudo...."
869 sudo "$ThisScript" "$@"
870 if [[ $? != 0 ]] ; then
871 echo "This script must be run as root (or using sudo). Exiting!"
872 exit 1
873 else
874 exit 0
875 fi
876 fi
877 CheckForFiles
878 if [[ $OSName == 'Darwin' ]] ; then
879 if [[ "$ShimSource" != "none" ]] ; then
880 echo "The --shim option is not supported on OS X! Exiting!"
881 exit 1
882 fi
883 if [[ "$LocalKeys" != 0 ]] ; then
884 echo "The --localkeys option is not supported on OS X! Exiting!"
885 exit 1
886 fi
887 InstallOnOSX $1
888 elif [[ $OSName == 'Linux' ]] ; then
889 InstallOnLinux
890 else
891 echo "Running on unknown OS; aborting!"
892 fi
893
894 if [[ $Problems ]] ; then
895 echo
896 echo "ALERT:"
897 echo "Installation has completed, but problems were detected. Review the output for"
898 echo "error messages and take corrective measures as necessary. You may need to"
899 echo "re-run this script or install manually before rEFInd will work."
900 echo
901 else
902 echo
903 echo "Installation has completed successfully."
904 echo
905 fi
906
907 if [[ $UnmountEsp ]] ; then
908 echo "Unmounting install dir"
909 umount $InstallDir
910 fi
911
912 if [[ "$InstallDir" == /tmp/refind_install ]] ; then
913 # sleep 5
914 rmdir "$InstallDir"
915 fi