syslinux-install_update 13 KB

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