syslinux-install_update 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. #!/usr/bin/bash
  2. #
  3. # Syslinux Installer / Updater Script (for BIOS only)
  4. # Copyright (C) 2011-2013 Matthew Gyurgyik <pyther@pyther.net>
  5. # Copyright (C) 2013 Keshav Padram Amburay <(the) (ddoott) (ridikulus) (ddoott) (rat) (aatt) (gemmaeiil) (ddoott) (ccoomm)>
  6. #
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU General Public License
  9. # as published by the Free Software Foundation; either version 2
  10. # of the License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. #
  21. #-----------------
  22. # ChangeLog:
  23. # 2013-10-23 : Keshav Padram Amburay : Updated script to work with Syslinux 6.02 Arch Linux pkg
  24. #-----------------
  25. # Exit Codes:
  26. # 1 - get_boot_device or other function failed
  27. # 2 - install/update failed
  28. # 3 - set_active failed
  29. # 4 - install_mbr failed
  30. #-----------------
  31. shopt -s nullglob
  32. bios_libpath="/usr/lib/syslinux/bios"
  33. bios_bootpath="/boot/syslinux"
  34. EXTLINUX="/usr/bin/extlinux"
  35. bios_autoupdate_file="/boot/syslinux/SYSLINUX_AUTOUPDATE"
  36. pciids_file="/usr/share/hwdata/pci.ids"
  37. ## Helper functions ##
  38. # Taken from libui-sh
  39. # $1 needle
  40. # $2 set (array) haystack
  41. check_is_in() {
  42. local needle="$1" element
  43. shift
  44. for element; do
  45. [[ $element = $needle ]] && return 0
  46. done
  47. return 1
  48. }
  49. get_disk() {
  50. local part=$1
  51. if [[ ! -b "${part}" ]]; then
  52. echo >&2 "error: '$part' is not a valid block device!"
  53. exit 1
  54. fi
  55. case "$part" in
  56. # catch cases like mmcblk0p1 and loop0p3
  57. *[[:digit:]]p[[:digit:]]*)
  58. local disk="${part%p[[:digit:]]}" # get everything before p1
  59. ;;
  60. *)
  61. local disk="${part%%[[:digit:]]*}"
  62. ;;
  63. esac
  64. if [[ ! -b "${disk}" ]]; then
  65. echo >&2 "error: '$disk' is not a valid block device!"
  66. exit 1
  67. fi
  68. echo $disk
  69. }
  70. # return true when blockdevice is an md raid, otherwise return a unset value
  71. # get all devices that are part of raid device $1
  72. device_is_raid() {
  73. [[ $1 && -f /proc/mdstat ]] || return 1
  74. local devmajor=$(stat -c %t "$1")
  75. (( devmajor == 9 ))
  76. }
  77. mdraid_all_slaves() {
  78. local slave slaves
  79. for slave in /sys/class/block/${1##*/}/slaves/*; do
  80. source "$slave/uevent"
  81. slaves="$slaves/dev/$DEVNAME "
  82. unset DEVNAME
  83. done
  84. echo $slaves
  85. }
  86. # Check /sys/block to see if device is partitioned
  87. # If we have a partitioned block device (sda1) /sys/block/sda1/dev will not exist
  88. # However, if we have an unpartitioned block device (sda) /sys/block/sda/dev will exist
  89. dev_is_part() {
  90. # $1 - blockdevice
  91. local dev=$1
  92. # If block device uevent file should be found
  93. # If a partition is passed in path shouldn't exist
  94. if [[ $dev = *cciss* ]]; then
  95. [[ -f /sys/block/cciss\!${dev##*/}/dev ]] && return 1
  96. elif [[ $dev = *ida* ]]; then
  97. [[ -f /sys/block/ida\!${dev##*/}/dev ]] && return 1
  98. else
  99. [[ -f /sys/block/${dev##*/}/dev ]] && return 1
  100. fi
  101. return 0
  102. }
  103. # If EFI PART is present in the first 8 bytes then it must be a GPT disk
  104. device_is_gpt() {
  105. local partsig=$(dd if="$1" skip=64 bs=8 count=1 2>/dev/null)
  106. [[ $partsig = "EFI PART" ]]
  107. }
  108. clear_gpt_attr2() {
  109. # $1 - Block Device, no partitions
  110. local disk=$1
  111. # Special Exception for cciss controllers
  112. if [[ $disk = *cciss* ]]; then
  113. for part in /dev/cciss/${disk##*/}*p*; do
  114. local partnum="${part##*[[:alpha:]]}"
  115. sgdisk "$disk" --attributes="$partnum":clear:2 &>/dev/null
  116. done
  117. # Smart 2 Controllers
  118. elif [[ $disk = *ida* ]]; then
  119. for part in /dev/ida/${disk##*/}*p*; do
  120. local partnum="${part##*[[:alpha:]]}"
  121. sgdisk "$disk" --attributes="$partnum":clear:2 &>/dev/null
  122. done
  123. else
  124. for part in /sys/block/${disk##*/}/${disk##*/}*; do
  125. local partnum="${part##*[[:alpha:]]}"
  126. sgdisk "$disk" --attributes="$partnum":clear:2 &>/dev/null
  127. done
  128. fi
  129. return 0
  130. }
  131. usage() {
  132. cat << EOF
  133. usage: $0 options
  134. This script will install or upgrade Syslinux (for BIOS only)
  135. OPTIONS:
  136. -h Show this message
  137. -i Install Syslinux
  138. -u Update Syslinux
  139. -a Set Boot flag on boot partiton
  140. -m Install Syslinux MBR
  141. -s Updates Syslinux if /boot/syslinux/SYSLINUX_AUTOUPDATE exists
  142. Arguments Required:
  143. -c Chroot install (ex: -c /mnt)
  144. Example Usage: $0 -i -a -m # (install, set boot flag, install mbr)
  145. $0 -u # (update)
  146. EOF
  147. }
  148. # Trys to find the partition that /boot resides on
  149. # This will either be on /boot or / (root)
  150. getBoot() {
  151. if [[ ! -d "$bios_bootpath" ]]; then
  152. echo "Could not find $bios_bootpath"
  153. echo "Is boot mounted? Is Syslinux installed?"
  154. exit 1
  155. fi
  156. syslinux_fs=(ext2 ext3 ext4 btrfs vfat xfs)
  157. # Use DATA from findmnt see rc.sysint for more info
  158. if [[ -f /proc/self/mountinfo ]]; then
  159. read rootdev rootfs < <(findmnt -run -t noautofs -o SOURCE,FSTYPE "$CHROOT/")
  160. read bootdev bootfs < <(findmnt -run -t noautofs -o SOURCE,FSTYPE "$CHROOT/boot")
  161. else
  162. echo "Could not find /proc/self/mountinfo"
  163. echo "Are you running a kernel greater than 2.6.24?"
  164. exit 1
  165. fi
  166. if [[ $bootfs ]]; then
  167. if ! check_is_in "$bootfs" "${syslinux_fs[@]}"; then
  168. echo "/boot file system is not supported by Syslinux"
  169. exit 1
  170. fi
  171. boot="boot"
  172. bootpart="$(readlink -f "$bootdev")"
  173. elif [[ $rootfs ]]; then
  174. if ! check_is_in "$rootfs" "${syslinux_fs[@]}"; then
  175. echo "/ (root) file system is not supported by Syslinux"
  176. exit 1
  177. fi
  178. boot="root"
  179. bootpart="$(readlink -f "$rootdev")"
  180. else
  181. echo "Could not find filesystem on / (root) or /boot."
  182. exit 1
  183. fi
  184. }
  185. # We store the partition table type either gpt or mbr in var ptb
  186. # In rare cases a user could have one raid disk using mbr and another using gpt
  187. # In such cases we accept that the output may be incomplete
  188. # Calls get_ptb() for $bootpart or for all device in RAID
  189. declare -A bootdevs
  190. get_boot_devices() {
  191. if device_is_raid "$bootpart"; then
  192. slaves=$(mdraid_all_slaves "$bootpart")
  193. for slave in ${slaves[@]}; do
  194. local disk=$(get_disk "$slave")
  195. device_is_gpt "$disk" && local ptb="GPT" || local ptb="MBR"
  196. bootdevs[$slave]="$ptb"
  197. done
  198. else
  199. local disk=$(get_disk "$bootpart")
  200. device_is_gpt "$disk" && local ptb="GPT" || local ptb="MBR"
  201. bootdevs[$bootpart]="$ptb"
  202. fi
  203. }
  204. # Function Assumes the boot partition should be marked as active
  205. # All other partitions should not have the boot flag set
  206. set_active() {
  207. # If any bootdev is a block device without partitions bail
  208. # we want to set the boot flag on partitioned disk
  209. for dev in "${!bootdevs[@]}"; do
  210. dev_is_part $dev || { echo "$dev does not contain partition table. Aborting set_active!"; return 1; }
  211. done
  212. # Clear BIOS Bootable Legacy Attribute for GPT drives
  213. # In rare cases where a RAID device has slaves on the same block device
  214. # Attribute 2 will be cleared for each partition multiple times
  215. for dev in "${!bootdevs[@]}"; do
  216. local ptb="${bootdevs[$dev]}"
  217. if [[ "$ptb" = GPT ]]; then
  218. local disk=$(get_disk "$dev")
  219. clear_gpt_attr2 "$disk"
  220. fi
  221. done
  222. # Set the boot flag on bootdevs (generated from get_boot_devices)
  223. for part in "${!bootdevs[@]}"; do
  224. local ptb="${bootdevs[$part]}"
  225. local partnum="${part##*[[:alpha:]]}"
  226. local disk=$(get_disk "$part")
  227. if [[ "$ptb" = MBR ]]; then
  228. if sfdisk "$disk" --activate "$partnum" &>/dev/null; then
  229. echo "Boot Flag Set - $part"
  230. else
  231. echo "FAILED to Set the boot flag on $part"
  232. exit 3
  233. fi
  234. elif [[ "$ptb" = GPT ]]; then
  235. if [[ ! -e /usr/bin/sgdisk ]]; then
  236. echo "FAILED to set attribute Legacy BIOS Bootable. sgdisk is not found - please install 'gptfdisk' package."
  237. exit 3
  238. fi
  239. if sgdisk "$disk" --attributes="$partnum":set:2 &>/dev/null; then
  240. echo "Attribute Legacy Bios Bootable Set - $part"
  241. else
  242. echo "FAILED to set attribute Legacy BIOS Bootable on $part"
  243. exit 3
  244. fi
  245. fi
  246. done
  247. return 0
  248. }
  249. install_mbr() {
  250. # If any bootdev is a block device without partitions bail
  251. # we want to install the mbr to a partitioned disk
  252. for dev in "${!bootdevs[@]}"; do
  253. dev_is_part "$dev" || { echo "$dev does not contain partition table. Aborting MBR install."; return 1; }
  254. done
  255. for part in "${!bootdevs[@]}"; do
  256. local partnum="${part##*[[:alpha:]]}"
  257. local disk=$(get_disk "$part")
  258. local ptb="${bootdevs[$part]}"
  259. # We want to install to the root of the block device
  260. # If the device is a partition - ABORT!
  261. dev_is_part "$disk" && \
  262. { echo "ABORT! MBR installation to partition ($disk)!"; exit 4;}
  263. if [[ "$ptb" = MBR ]]; then
  264. mbrfile="$bios_libpath/mbr.bin"
  265. elif [[ "$ptb" = GPT ]]; then
  266. mbrfile="$bios_libpath/gptmbr.bin"
  267. fi
  268. if dd bs=440 count=1 conv=notrunc if="$mbrfile" of="$disk" &> /dev/null; then
  269. echo "Installed MBR ($mbrfile) to $disk"
  270. else
  271. echo "Error Installing MBR ($mbrfile) to $disk"
  272. exit 4
  273. fi
  274. done
  275. return 0
  276. }
  277. install_modules() {
  278. # Copy all syslinux *.c32 modules to /boot
  279. rm "$bios_bootpath"/*.c32 &> /dev/null
  280. cp "$bios_libpath"/*.c32 "$bios_bootpath"/ &> /dev/null
  281. # Copy / Symlink pci.ids if pci.ids exists on the FS
  282. if [[ -f "$pciids_file" ]]; then
  283. rm "$bios_bootpath/pci.ids" &> /dev/null
  284. cp "$pciids_file" "$bios_bootpath/pci.ids" &> /dev/null
  285. fi
  286. }
  287. _install() {
  288. install_modules
  289. if device_is_raid "$bootpart" ; then
  290. echo "Detected RAID on /boot - installing Syslinux with --raid"
  291. "$EXTLINUX" --install "$bios_bootpath" --raid &> /dev/null
  292. else
  293. "$EXTLINUX" --install "$bios_bootpath" &> /dev/null
  294. fi
  295. if (( $? )); then
  296. echo "Syslinux BIOS install failed"
  297. exit 2
  298. else
  299. echo "Syslinux BIOS install successful"
  300. fi
  301. touch "$CHROOT/$bios_autoupdate_file"
  302. }
  303. update() {
  304. install_modules
  305. if device_is_raid "$bootpart" ; then
  306. echo "Detected RAID on /boot - updating Syslinux with --raid"
  307. "$EXTLINUX" --update "$bios_bootpath" --raid &> /dev/null
  308. else
  309. "$EXTLINUX" --update "$bios_bootpath" &> /dev/null
  310. fi
  311. if (($?)); then
  312. echo "Syslinux BIOS update failed"
  313. exit 2
  314. else
  315. echo "Syslinux BIOS update successful"
  316. fi
  317. }
  318. if (( $# == 0 )); then
  319. usage
  320. exit 1
  321. fi
  322. while getopts "c:uihmas" opt; do
  323. case $opt in
  324. c)
  325. CHROOT=$(readlink -e "$OPTARG")
  326. if [[ -z $CHROOT ]]; then
  327. echo "error: chroot path ``$OPTARG does not exist";
  328. exit 1
  329. fi
  330. ;;
  331. h)
  332. USAGE="True"
  333. ;;
  334. i)
  335. INSTALL="True"
  336. ;;
  337. u)
  338. UPDATE="True"
  339. ;;
  340. m)
  341. MBR="True"
  342. ;;
  343. a)
  344. SET_ACTIVE="True"
  345. ;;
  346. s)
  347. # If AUTOUPDATE_FILE does not exist exit the script
  348. if [[ -f $bios_autoupdate_file ]]; then
  349. UPDATE="True"
  350. else
  351. exit 0
  352. fi
  353. ;;
  354. *)
  355. usage
  356. exit 1
  357. ;;
  358. esac
  359. done
  360. if [[ $USAGE ]]; then
  361. usage
  362. exit 0
  363. fi
  364. # Display Usage Information if both Install and Update are passed
  365. if [[ $INSTALL && $UPDATE ]]; then
  366. usage
  367. exit 1
  368. fi
  369. # Make sure only root can run our script
  370. if (( $(id -u) != 0 )); then
  371. echo "This script must be run as root" 1>&2
  372. exit 1
  373. fi
  374. # If a chroot dir is path set variables to reflect chroot
  375. if [[ "$CHROOT" ]]; then
  376. bios_libpath="$CHROOT$bios_libpath"
  377. bios_bootpath="$CHROOT$bios_bootpath"
  378. EXTLINUX="$CHROOT$EXTLINUX"
  379. fi
  380. # Exit if no /boot path exists
  381. if ( f=("$bios_bootpath"/*); (( ! ${#f[@]} )) ); then
  382. echo "Error: $bios_bootpath is empty!"
  383. echo "Is /boot mounted?"
  384. exit 1
  385. fi
  386. # Get the boot device if any of these options are passed
  387. if [[ $INSTALL || $UPDATE || $SET_ACTIVE || $MBR ]]; then
  388. getBoot
  389. fi
  390. # Install or Update
  391. if [[ $INSTALL ]]; then
  392. _install || exit
  393. elif [[ $UPDATE ]]; then
  394. update || exit
  395. fi
  396. # SET_ACTIVE and MBR
  397. if [[ $SET_ACTIVE ]] || [[ $MBR ]]; then
  398. get_boot_devices
  399. if [[ $SET_ACTIVE ]]; then
  400. set_active || exit
  401. fi
  402. if [[ $MBR ]]; then
  403. install_mbr || exit
  404. fi
  405. fi
  406. exit 0