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