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