├── .gitignore ├── prepare.d ├── gparted.sh ├── rocky.sh ├── archlinux.sh ├── fedora.sh ├── debian.sh ├── netbsd │ └── rc.miniroot ├── clonezilla.sh ├── ubuntu.sh ├── memtest.sh ├── freebsd.sh └── netbsd.sh ├── menu.d ├── reboot.sh ├── netbsd.sh ├── gparted.sh ├── rocky.sh ├── debian.sh ├── archlinux.sh ├── clonezilla.sh ├── freebsd.sh ├── tool.sh ├── fedora.sh └── ubuntu.sh ├── grub.example ├── grub.cfg.d │ └── default └── grub.cfg ├── README.md ├── simple-pxe-in-docker ├── config.local.example ├── LICENSE ├── functions.sh └── simple-pxe /.gitignore: -------------------------------------------------------------------------------- 1 | config.local 2 | -------------------------------------------------------------------------------- /prepare.d/gparted.sh: -------------------------------------------------------------------------------- 1 | clonezilla.sh -------------------------------------------------------------------------------- /menu.d/reboot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #menu: Reboot 3 | 4 | echo reboot 5 | -------------------------------------------------------------------------------- /grub.example/grub.cfg.d/default: -------------------------------------------------------------------------------- 1 | configfile (http,10.123.123.1)/pxe/menu/root.menu 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple-pxe: A simple GRUB netboot menu generator 2 | 3 | Configuration: See `config.local.sample` for example. 4 | 5 | Prepare files (network needed): 6 | ``` 7 | ./simple-pxe prepare 8 | ``` 9 | 10 | Update menu (no network needed): 11 | ``` 12 | ./simple-pxe menu 13 | ``` 14 | -------------------------------------------------------------------------------- /simple-pxe-in-docker: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source config.local 4 | 5 | docker run --rm -i \ 6 | -v "$PXE_LOCAL_ROOT:$PXE_LOCAL_ROOT" \ 7 | -v "$PWD:/simple-pxe:ro" \ 8 | -e DEBIAN_FRONTEND=noninteractive \ 9 | ustclug/ubuntu:24.04 \ 10 | /bin/bash -x -e <> "${temp[rlist]}" 16 | count=$((count + 1)) 17 | done 18 | done 19 | 20 | (("${count}" != 0)) && mv "${temp[rlist]}" "release-list" 21 | 22 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 23 | -------------------------------------------------------------------------------- /prepare.d/archlinux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #root: archlinux 3 | 4 | source functions.sh 5 | cd "${LOCAL_PATH}" || exit 1 6 | 7 | text=$(curl -s "${ARCHLINUX_MIRROR}/iso/latest/sha256sums.txt") 8 | 9 | if [[ "${text}" =~ archlinux-([0-9.]+)-x86_64.iso ]]; then 10 | iso_file="${BASH_REMATCH[0]}" 11 | folder="${BASH_REMATCH[1]}" 12 | url="${ARCHLINUX_MIRROR}/iso/latest/${iso_file}" 13 | 14 | if [[ -d "${folder}" ]]; then 15 | echo "Already exists: ${folder}" 16 | else 17 | echo "Extracting ${url} to ${folder}" 18 | net_extract "${url}" "${folder}" 19 | fi 20 | fi 21 | 22 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 23 | -------------------------------------------------------------------------------- /menu.d/netbsd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #menu: NetBSD 3 | #root: netbsd 4 | 5 | source functions.sh 6 | cd "${LOCAL_PATH}" || exit 1 7 | 8 | while read -r folder; do 9 | IFS='_' read -r version arch <<< "${folder}" 10 | 11 | cat <<- EOF 12 | menuentry 'NetBSD ${version} (${arch}): full distribution' { 13 | echo "Loading kernel..." 14 | knetbsd ${GRUB_PATH}/${folder}/netbsd 15 | echo "Loading miniroot..." 16 | knetbsd_module_elf ${GRUB_PATH}/${folder}/miniroot.kmod.gz 17 | } 18 | menuentry 'NetBSD ${version} (${arch}): installation kernel' { 19 | echo "Loading kernel..." 20 | knetbsd ${GRUB_PATH}/${folder}/netbsd-INSTALL.gz 21 | } 22 | EOF 23 | done < <(ls | sort -t_ -k1,1r -k2) 24 | 25 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 26 | -------------------------------------------------------------------------------- /menu.d/gparted.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #menu: GParted Live 3 | #root: gparted 4 | 5 | source functions.sh 6 | cd "${LOCAL_PATH}" || exit 1 7 | 8 | while read -r folder; do 9 | IFS='_' read -r version arch <<< "${folder}" 10 | 11 | kernels=("${folder}/live/vmlinuz"*) 12 | initrds=("${folder}/live/initrd"*) 13 | [[ -f "${kernels[0]}" && -f "${initrds[0]}" ]] || continue 14 | 15 | grub_linux_entry \ 16 | "Clonezilla Live ${version} (${arch})" \ 17 | "${GRUB_PATH}/${kernels[0]}" \ 18 | "${GRUB_PATH}/${initrds[0]}" \ 19 | "boot=live netboot=nfs nfsroot=${PXE_NFS_HOST}:${NFS_PATH}/${folder}/ union=overlay username=user config quiet components noswap net.ifnames=0 nosplash" 20 | done < <(ls | sort -t_ -k1,1r) 21 | 22 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 23 | -------------------------------------------------------------------------------- /menu.d/rocky.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #menu: Rocky 3 | #root: rocky 4 | 5 | source functions.sh 6 | cd "${LOCAL_PATH}" || exit 1 7 | 8 | grub_option SP_ROCKY_ARCH Architecture \ 9 | x86_64 x86_64 "For most modern PCs" 10 | 11 | IFS=' ' read -r -a mirrors <<< "${ROCKY_MIRROR_BACKUP}" 12 | grub_mirror_selector SP_ROCKY_MIRROR "${ROCKY_MIRROR}" "${mirrors[@]}" 13 | 14 | while read -r version arch; do 15 | base="(\$mirror_protocol,\$mirror_host)/\$mirror_path/${version}/BaseOS/${arch}/os/images/pxeboot" 16 | repo_arg="inst.repo" 17 | 18 | echo "if [ \$SP_ROCKY_ARCH = ${arch} ]; then" 19 | grub_linux_entry \ 20 | "Rocky ${version} Installer" \ 21 | "${base}/vmlinuz" \ 22 | "${base}/initrd.img" \ 23 | "${repo_arg}=\$SP_ROCKY_MIRROR/${version}/BaseOS/${arch}/os" 24 | echo "fi" 25 | done < <(tac release-list) 26 | 27 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 28 | -------------------------------------------------------------------------------- /menu.d/debian.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #menu: Debian 3 | #root: debian 4 | 5 | source functions.sh 6 | cd "${LOCAL_PATH}" || exit 1 7 | 8 | grub_option SP_DEBIAN_ARCH Architecture \ 9 | amd64 amd64 "For most modern PCs" \ 10 | i386 i386 "For very old PCs" 11 | 12 | IFS=' ' read -r -a mirrors <<< "${DEBIAN_MIRROR_BACKUP}" 13 | grub_mirror_selector SP_DEBIAN_MIRROR "${DEBIAN_MIRROR}" "${mirrors[@]}" 14 | 15 | fmt="(\$mirror_protocol,\$mirror_host)/\$mirror_path/dists/%s/main/installer-\${SP_DEBIAN_ARCH}/current/images/netboot/debian-installer/\${SP_DEBIAN_ARCH}" 16 | while read -r codename status version; do 17 | grub_linux_entry \ 18 | "Debian ${codename} (${status}) Installer" \ 19 | "$(printf "${fmt}" "${codename}")/linux" \ 20 | "$(printf "${fmt}" "${codename}")/initrd.gz" \ 21 | "url=${HTTP_PATH}/preseed.txt" 22 | done < <(sort -k3,3nr -k2,2 release-list) 23 | 24 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 25 | -------------------------------------------------------------------------------- /menu.d/archlinux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #menu: Arch Linux 3 | #root: archlinux 4 | 5 | source functions.sh 6 | cd "${LOCAL_PATH}" || exit 1 7 | 8 | grub_option SP_ARCH_COPYTORAM "Copy RootFS to RAM" \ 9 | y yes "Copy RootFS to RAM" \ 10 | n no "Mount RootFS via NFS" 11 | 12 | IFS=' ' read -r -a mirrors <<< "${ARCHLINUX_MIRROR_BACKUP}" 13 | grub_mirror_selector SP_ARCH_MIRROR "${ARCHLINUX_MIRROR}" "${mirrors[@]}" 14 | 15 | while read -r version; do 16 | bootdir="${GRUB_PATH}/${version}/arch/boot" 17 | grub_linux_entry \ 18 | "Arch Linux (${version})" \ 19 | "${bootdir}/x86_64/vmlinuz-linux" \ 20 | "${bootdir}/x86_64/initramfs-linux.img ${bootdir}/amd-ucode.img ${bootdir}/intel-ucode.img" \ 21 | "ip=dhcp archisobasedir=arch archiso_nfs_srv=${PXE_NFS_HOST}:${NFS_PATH}/${version} copytoram=\$SP_ARCH_COPYTORAM mirror=\$SP_ARCH_MIRROR" 22 | done < <(ls | sort -ru) 23 | 24 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 25 | -------------------------------------------------------------------------------- /config.local.example: -------------------------------------------------------------------------------- 1 | PXE_PREPARE=(archlinux ubuntu debian rocky fedora freebsd netbsd clonezilla gparted memtest) 2 | PXE_MENUS=( 3 | "-Distros" archlinux ubuntu debian rocky fedora freebsd netbsd 4 | "-Utilities" clonezilla gparted tool reboot) 5 | 6 | PXE_LOCAL_ROOT=/srv/pxe 7 | PXE_HTTP_ROOT=http://10.123.123.1/pxe 8 | PXE_NFS_HOST=10.123.123.1 9 | PXE_NFS_ROOT=/srv/pxe 10 | 11 | PXE_TITLE="USTC Network Boot Service (http://pxe.ustc.edu.cn/)" 12 | PXE_INTRO=" 13 | == USTC Network Boot Service == 14 | 15 | Website: http://pxe.ustc.edu.cn/ 16 | 17 | This is the USTC Network Boot Service provided by: 18 | - Linux User Group (https://lug.ustc.edu.cn/) 19 | - Network and Information Center (https://ustcnet.ustc.edu.cn/) 20 | - Library (https://lib.ustc.edu.cn) 21 | 22 | If you get here by mistake, please reboot, check the BIOS/UEFI firmware 23 | settings and disable network boot. 24 | 25 | If you need help, please contact lug@ustc.edu.cn." 26 | -------------------------------------------------------------------------------- /menu.d/clonezilla.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #menu: Clonezilla 3 | #root: clonezilla 4 | 5 | source functions.sh 6 | cd "${LOCAL_PATH}" || exit 1 7 | 8 | while read -r folder; do 9 | IFS='_' read -r version arch <<< "${folder}" 10 | 11 | kernels=("${folder}/live/vmlinuz"*) 12 | initrds=("${folder}/live/initrd"*) 13 | [[ -f "${kernels[0]}" && -f "${initrds[0]}" ]] || continue 14 | 15 | grub_linux_entry \ 16 | "Clonezilla Live ${version} (${arch})" \ 17 | "${GRUB_PATH}/${kernels[0]}" \ 18 | "${GRUB_PATH}/${initrds[0]}" \ 19 | "boot=live netboot=nfs nfsroot=${PXE_NFS_HOST}:${NFS_PATH}/${folder} union=overlay username=user hostname=clonezilla config quiet components noswap edd=on nomodeset noeject ocs_live_run=ocs-live-general ocs_live_extra_param= ocs_live_batch=no vga=788 net.ifnames=0 splash i915.blacklist=yes radeonhd.blacklist=yes nouveau.blacklist=yes vmwgfx.enable_fbdev=1" 20 | done < <(ls | sort -t_ -k1,1r) 21 | 22 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 23 | -------------------------------------------------------------------------------- /prepare.d/fedora.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #root: fedora 3 | 4 | source functions.sh 5 | cd "${LOCAL_PATH}" || exit 1 6 | 7 | temp[rlist]=$(mktemp) 8 | 9 | count=0 10 | for ((version=38; version<100; version++)); do 11 | url_base="${FEDORA_MIRROR}/releases/${version}" 12 | url_check "${url_base}/" 403 || break 13 | url="" 14 | 15 | for ((r=0; r<20; r++)); do 16 | rev="1.${r}" 17 | _url="${url_base}/Workstation/x86_64/iso/Fedora-Workstation-Live-x86_64-${version}-${rev}.iso" 18 | url_check "${_url}" || continue 19 | url=${_url} && break 20 | done 21 | 22 | if [[ -n "${url}" ]]; then 23 | target="Workstation_Live_${version}_${rev}" 24 | 25 | if [[ -d "${target}" ]]; then 26 | echo "Already exists: ${target}" 27 | else 28 | echo "Extracting ${url} to ${target}" 29 | net_extract "${url}" "${target}" 30 | fi 31 | 32 | echo -e "${version}\t${rev}" >> "${temp[rlist]}" 33 | count=$((count + 1)) 34 | fi 35 | done 36 | 37 | (("${count}" != 0)) && mv "${temp[rlist]}" "release-list" 38 | 39 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 40 | -------------------------------------------------------------------------------- /menu.d/freebsd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #menu: FreeBSD 3 | #root: freebsd 4 | 5 | source functions.sh 6 | cd "${LOCAL_PATH}" || exit 1 7 | 8 | while read -r folder; do 9 | IFS='_' read -r version arch <<< "${folder}" 10 | [[ ${arch} == "i386" ]] && grub_cpu=i386 || grub_cpu=x86_64 11 | 12 | cat <<- EOF 13 | if [ \$grub_platform != efi -o \$grub_cpu = ${grub_cpu} ]; then 14 | menuentry 'FreeBSD ${version}-RELEASE (${arch})' { 15 | if [ \$grub_platform = efi ]; then 16 | chainloader ${GRUB_PATH}/${folder}/boot/loader_pxe.efi 17 | else 18 | echo "Loading kernel..." 19 | kfreebsd ${GRUB_PATH}/${folder}/boot/kernel/kernel 20 | kfreebsd_loadenv ${GRUB_PATH}/${folder}/boot/device.hints 21 | echo "Loading rootfs image..." 22 | kfreebsd_module ${GRUB_PATH}/${folder}/cd.iso type=mfs_root 23 | set kFreeBSD.vfs.root.mountfrom="cd9660:/dev/md0" 24 | set kFreeBSD.vfs.root.mountfrom.options=ro 25 | fi 26 | } 27 | fi 28 | EOF 29 | done < <(ls | sort -t_ -k1,1r -k2) 30 | 31 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 CUI Hao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /prepare.d/debian.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #root: debian 3 | 4 | source functions.sh 5 | cd "${LOCAL_PATH}" || exit 1 6 | 7 | temp[preseed]=$(mktemp) 8 | cat > "${temp[preseed]}" <<- EOF && install -m644 "${temp[preseed]}" preseed.txt 9 | d-i mirror/country string manual 10 | d-i mirror/http/hostname string $(cut -d/ -f3 <<< "${DEBIAN_MIRROR}") 11 | d-i mirror/http/directory string /$(cut -d/ -f4- <<< "${DEBIAN_MIRROR}") 12 | d-i mirror/http/proxy string 13 | d-i apt-setup/security_host string $(cut -d/ -f3 <<< "${DEBIAN_MIRROR}") 14 | EOF 15 | 16 | temp[rlist]=$(mktemp) 17 | count=0 18 | status_list=(unstable testing stable oldstable oldoldstable) 19 | 20 | for status in "${status_list[@]}"; do 21 | while read -r line; do 22 | IFS=": " read -r key value <<< "${line}" 23 | if [[ "${key}" == Version ]]; then 24 | version="${value}" 25 | elif [[ "${key}" == Codename ]]; then 26 | echo -e "${value}\t${status}\t${version}" >> "${temp[rlist]}" 27 | count=$((count + 1)) 28 | break 29 | fi 30 | done < <(curl -fsL "${DEBIAN_MIRROR}/dists/${status}/Release") 31 | done 32 | 33 | if (("${count}" == "${#status_list[@]}")); then 34 | mv "${temp[rlist]}" "release-list" 35 | else 36 | panick "Failed to update Debian version list" 37 | fi 38 | 39 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 40 | -------------------------------------------------------------------------------- /prepare.d/netbsd/rc.miniroot: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | export PATH=/rescue:/tmp 5 | 6 | ## Prepare RW paths {{{{ 7 | mount -t tmpfs -o union tmpfs /etc 8 | mount -t tmpfs tmpfs /tmp 9 | mount -t tmpfs tmpfs /var 10 | mkdir -p /var/run /var/db 11 | ## }}} 12 | 13 | ## Extract files from official miniroot.fs {{{{ 14 | bzcat /miniroot.fs.bz2 > /tmp/miniroot.fs 15 | vnconfig vnd0 /tmp/miniroot.fs 16 | 17 | mkdir /tmp/mnt 18 | mount /dev/vnd0a /tmp/mnt 19 | 20 | cp /tmp/mnt/sbin/dhcpcd /tmp 21 | cp -Rp /tmp/mnt/etc/* /etc 22 | 23 | umount /tmp/mnt 24 | vnconfig -u vnd0 25 | rm /tmp/miniroot.fs 26 | ## }}} 27 | 28 | ## DHCP, mount NFS and mount ISO {{{ 29 | new_root="/tmp/newroot" 30 | 31 | dhcpcd -w -b -q -c /dev/null 32 | mount_nfs "${nfs_path}" /tmp/mnt 33 | 34 | vnconfig -r vnd0 /tmp/mnt/cd.iso 35 | mkdir "${new_root}" 36 | mount_cd9660 /dev/vnd0a "${new_root}" 37 | ## }}} 38 | 39 | ## NetBSD 7.x Fix {{{ 40 | if ! [ -d "${new_root}/rescue" ]; then 41 | mount_tmpfs -o union tmpfs "${new_root}" 42 | mkdir "${new_root}/rescue" 43 | cp -p /rescue/sh "${new_root}/rescue" 44 | fi 45 | 46 | if ! [ -e "${new_root}/dev/console" ]; then 47 | mount_tmpfs tmpfs "${new_root}/dev" 48 | cp -p /dev/MAKEDEV "${new_root}/dev" 49 | chroot "${new_root}" /rescue/sh -c 'cd /dev && ./MAKEDEV all' 50 | fi 51 | ## }}} 52 | 53 | sysctl -w init.root="${new_root}" 54 | -------------------------------------------------------------------------------- /prepare.d/clonezilla.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source functions.sh 4 | cd "${LOCAL_PATH}" || exit 1 5 | 6 | join_by() { 7 | local IFS="$1" 8 | shift 9 | echo "$*" 10 | } 11 | 12 | prepare() { 13 | local file_regex="$1" 14 | local mirror_url="$2" 15 | local filelist_url="$3" 16 | 17 | while read -r filename; do 18 | if [[ "${filename}" =~ ${file_regex} ]]; then 19 | url="${mirror_url}/${filename}" 20 | url_check "${url}" || continue 21 | target="$(join_by _ "${BASH_REMATCH[@]:1}")" 22 | 23 | if [[ -d "${target}" ]]; then 24 | echo "Already exists: ${target}" 25 | else 26 | echo "Extracting ${url} to ${target}" 27 | net_extract "${url}" "${target}" 28 | fi 29 | fi 30 | done < <(grep_web "${filelist_url}" "${file_regex}") 31 | } 32 | 33 | case "$(basename -- "$0")" in 34 | clonezilla.sh) 35 | prepare \ 36 | "clonezilla-live-([0-9]{8}-[a-z]+)-(amd64|i386).zip" \ 37 | "https://osdn.net/dl/clonezilla" \ 38 | "https://clonezilla.org/downloads/alternative/data/CHECKSUMS.TXT" 39 | ;; 40 | gparted.sh) 41 | prepare \ 42 | "gparted-live-([0-9.]+-[0-9]+)-(amd64|i686|i686-pae).zip" \ 43 | "https://downloads.sourceforge.net/gparted" \ 44 | "https://gparted.org/gparted-live/stable/CHECKSUMS.TXT" 45 | ;; 46 | *) 47 | panick 'Unknown repo!' 48 | ;; 49 | esac 50 | 51 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 52 | -------------------------------------------------------------------------------- /menu.d/tool.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #menu: Tools 3 | #root: tool 4 | 5 | source functions.sh 6 | 7 | do_memtest() { 8 | cd "${LOCAL_PATH}/memtest" || return 1 9 | 10 | binary=$(ls memtest.efi | sort -ru | head -n1) 11 | if [[ -f "${binary}" ]]; then 12 | cat <<- EOF 13 | if [ \$grub_platform = efi ]; then 14 | menuentry 'Memtest86+ (EFI)' { 15 | echo 'Loading...' 16 | chainloader ${GRUB_PATH}/memtest/${binary} 17 | } 18 | fi 19 | EOF 20 | fi 21 | 22 | binary=$(ls memtest.bin | sort -ru | head -n1) 23 | if [[ -f "${binary}" ]]; then 24 | cat <<- EOF 25 | if [ \$grub_platform = pc ]; then 26 | menuentry 'Memtest86+ (BIOS)' { 27 | echo 'Loading...' 28 | linux16 ${GRUB_PATH}/memtest/${binary} 29 | } 30 | fi 31 | EOF 32 | fi 33 | } 34 | 35 | do_uefishell() { 36 | cd "${PXE_LOCAL_ROOT}/archlinux" || return 1 37 | local latest=$(ls | sort -ru | head -n1) 38 | 39 | efipath="${latest}/EFI/BOOT/BOOTx64.EFI" 40 | if [[ -f "${efipath}" ]]; then 41 | relpath=$(realpath --relative-to="${PXE_LOCAL_ROOT}" "${efipath}") 42 | cat <<- EOF 43 | if [ \$grub_platform = efi ]; then 44 | menuentry 'UEFI Shell' { 45 | echo 'Loading...' 46 | chainloader $(url2grub "${PXE_HTTP_ROOT}/${relpath}") 47 | } 48 | fi 49 | EOF 50 | fi 51 | } 52 | 53 | do_memtest 54 | do_uefishell 55 | 56 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 57 | -------------------------------------------------------------------------------- /menu.d/fedora.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #menu: Fedora 3 | #root: fedora 4 | 5 | source functions.sh 6 | cd "${LOCAL_PATH}" || exit 1 7 | 8 | IFS=' ' read -r -a mirrors <<< "${FEDORA_MIRROR_BACKUP}" 9 | grub_mirror_selector SP_FEDORA_MIRROR "${FEDORA_MIRROR}" "${mirrors[@]}" 10 | 11 | grub_menu_sep '--- Live CD ---' 12 | 13 | while read -r folder; do 14 | IFS='_' read -r variant iso_type version rev <<< "${folder}" 15 | 16 | # Check LiveCD kernel existence 17 | kernels=("${folder}/images/pxeboot/vmlinuz"*) 18 | initrds=("${folder}/images/pxeboot/initrd"*) 19 | image="${folder}/LiveOS/squashfs.img" 20 | [[ -f "${kernels[0]}" && -f "${initrds[0]}" && -f "${image}" ]] || continue 21 | 22 | grub_linux_entry \ 23 | "Fedora ${version} ${variant} ${iso_type}" \ 24 | "${GRUB_PATH}/${kernels[0]}" \ 25 | "${GRUB_PATH}/${initrds[0]}" \ 26 | "root=live:${HTTP_PATH}/${image} ro rd.live.image rd.lvm=0 rd.luks=0 rd.md=0 rd.dm=0" 27 | done < <( ls | sort -t_ -k3nr ) 28 | 29 | grub_menu_sep '--- Network Installer ---' 30 | 31 | fmt="(\$mirror_protocol,\$mirror_host)/\$mirror_path/releases/%s/Everything/x86_64/os/images/pxeboot" 32 | while read -r version rel; do 33 | grub_linux_entry \ 34 | "Fedora ${version} installer" \ 35 | "$(printf "${fmt}" "${version}")/vmlinuz" \ 36 | "$(printf "${fmt}" "${version}")/initrd.img" \ 37 | "inst.repo=\$SP_FEDORA_MIRROR/releases/${version}/Everything/x86_64/os/" 38 | done < <( sort -k1nr release-list ) 39 | 40 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 41 | -------------------------------------------------------------------------------- /menu.d/ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #menu: Ubuntu 3 | #root: ubuntu 4 | 5 | source functions.sh 6 | cd "${LOCAL_PATH}" || exit 1 7 | 8 | grub_option SP_UBUNTU_ARCH Architecture \ 9 | amd64 amd64 "For most modern PCs" \ 10 | i386 i386 "For very old PCs" 11 | grub_option SP_UBUNTU_ROOTFS "LiveCD RootFS" \ 12 | toram ToRAM "Copy RootFS to RAM" \ 13 | "" NFS "Mount RootFS via NFS" 14 | 15 | IFS=' ' read -r -a mirrors <<< "${UBUNTU_MIRROR_BACKUP}" 16 | grub_mirror_selector SP_UBUNTU_MIRROR "${UBUNTU_MIRROR}" "${mirrors[@]}" 17 | 18 | grub_menu_sep '--- Live CD ---' 19 | 20 | while read -r folder; do 21 | IFS='_' read -r codename version variant arch <<< "${folder}" 22 | 23 | # Check LiveCD kernel existence 24 | kernels=("${folder}/casper/vmlinuz"*) 25 | initrds=("${folder}/casper/initrd"*) 26 | [[ -f "${kernels[0]}" && -f "${initrds[0]}" ]] || continue 27 | 28 | echo "if [ \$SP_UBUNTU_ARCH = ${arch} ]; then" 29 | grub_linux_entry \ 30 | "Ubuntu ${version} (${codename}) ${variant} LiveCD" \ 31 | "${GRUB_PATH}/${kernels[0]}" \ 32 | "${GRUB_PATH}/${initrds[0]}" \ 33 | "boot=casper ip=dhcp netboot=nfs nfsroot=${PXE_NFS_HOST}:${NFS_PATH}/${folder} locale=zh_CN \${SP_UBUNTU_ROOTFS}" 34 | echo "fi" 35 | done < <(ls | sort -t_ -k2,2r -k3) 36 | 37 | grub_menu_sep '--- Network Installer ---' 38 | 39 | fmt="(\$mirror_protocol,\$mirror_host)/\$mirror_path/dists/%s/main/installer-\${SP_UBUNTU_ARCH}/current/images/netboot/ubuntu-installer/\${SP_UBUNTU_ARCH}" 40 | while read -r version full_version support codename; do 41 | [[ "${support}" == 1 ]] && lts="LTS " || lts="" 42 | 43 | grub_linux_entry \ 44 | "Ubuntu ${version} ${lts}(${codename}) installer" \ 45 | "$(printf "${fmt}" "${codename}")/linux" \ 46 | "$(printf "${fmt}" "${codename}")/initrd.gz" \ 47 | "url=${HTTP_PATH}/preseed.txt" 48 | done < release-list 49 | 50 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 51 | -------------------------------------------------------------------------------- /prepare.d/ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #root: ubuntu 3 | 4 | source functions.sh 5 | cd "${LOCAL_PATH}" || exit 1 6 | 7 | temp[preseed]=$(mktemp) 8 | cat > "${temp[preseed]}" <<- EOF && install -m644 "${temp[preseed]}" preseed.txt 9 | d-i mirror/country string manual 10 | d-i mirror/http/hostname string $(cut -d/ -f3 <<< "${DEBIAN_MIRROR}") 11 | d-i mirror/http/directory string /$(cut -d/ -f4- <<< "${DEBIAN_MIRROR}") 12 | d-i mirror/http/proxy string 13 | EOF 14 | 15 | # Update Ubuntu release list 16 | regex="Ubuntu ([0-9]{2}\.[0-9]{2})(\.[0-9]+)? (LTS )?\(([A-Z][a-z]+) [A-Z][a-z]+\)" 17 | temp[rlist]=$(mktemp) 18 | 19 | grep_web "${UBUNTU_RELEASES_MIRROR}/HEADER.html" "${regex}" | while read -r line; do 20 | if [[ "${line}" =~ ${regex} ]]; then 21 | version="${BASH_REMATCH[1]}" 22 | full_version="${version}${BASH_REMATCH[2]}" 23 | [[ "${BASH_REMATCH[3]}" != "LTS " ]] 24 | support=$? 25 | codename="${BASH_REMATCH[4],,}" 26 | echo -e "${version}\t${full_version}\t${support}\t${codename}" 27 | fi 28 | done > "${temp[rlist]}" 29 | 30 | if [[ "${PIPESTATUS[0]}" == 0 && "$?" == 0 ]]; then 31 | mv "${temp[rlist]}" "release-list" 32 | fi 33 | 34 | # Update LiveCD 35 | while read -r version full_version support codename; do 36 | for arch in amd64 i386; do 37 | # Only desktop/live-server variants support casper boot 38 | for variant in desktop live-server; do 39 | iso="ubuntu-${full_version}-${variant}-${arch}.iso" 40 | url="${UBUNTU_RELEASES_MIRROR}/${version}/${iso}" 41 | target="${codename}_${full_version}_${variant}_${arch}" 42 | 43 | url_check "${url}" || continue 44 | 45 | if [[ -d "${target}" ]]; then 46 | echo "Already exists: ${target}" 47 | else 48 | echo "Extracting ${url} to ${target}" 49 | net_extract "${url}" "${target}" 50 | fi 51 | done 52 | done 53 | done < "release-list" 54 | 55 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 56 | -------------------------------------------------------------------------------- /prepare.d/memtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #root: tool/memtest 3 | 4 | source functions.sh 5 | cd "${LOCAL_PATH}" || exit 1 6 | 7 | # Memtest86+, FOSS, but not UEFI-compatible 8 | url_base="http://www.memtest.org" 9 | url_regex='download/[0-9.]+/memtest86\+-([0-9.]+)\.bin\.gz' 10 | output=$(curl -sfL "${url_base}" | grep -Eom1 "${url_regex}") 11 | if [[ "${output}" =~ ${url_regex} ]]; then 12 | version="${BASH_REMATCH[1]}" 13 | target="memtest86plus_${version}.bin" 14 | url="${url_base}/${output}" 15 | 16 | if [[ -f "${target}" ]]; then 17 | echo "Already exists: ${target}" 18 | else 19 | echo "Extracting ${url} to ${target}" 20 | 21 | temp[m86p]=".tmp.memtest86plus" 22 | curl -fsL "${url}" | gzip -d > "${temp[m86p]}" 23 | 24 | if [[ "${PIPESTATUS[0]}" == 0 && "$?" == 0 ]]; then 25 | mv "${temp[m86p]}" "${target}" 26 | else 27 | echo "Download failed" 28 | rm -f "${temp[m86p]}" 29 | fi 30 | fi 31 | fi 32 | 33 | # Memtest86, not FOSS, we only fetch UEFI image 34 | url_base="https://www.memtest86.com/" 35 | ver_regex='MemTest86 \Kv[0-9.]+(?! Free)' 36 | version=$(curl -sfL "${url_base}/download.htm" | grep -Pom1 "${ver_regex}") 37 | if [[ -n "${version}" ]]; then 38 | target="memtest86_${version}_x64.efi" 39 | temp[m86]=".tmp.memtest86" 40 | 41 | if [[ -f "${target}" ]]; then 42 | echo "Already exists: ${target}" 43 | else 44 | temp[m86]=".tmp.memtest86" 45 | url="${url_base}/downloads/memtest86-usb.zip" 46 | 47 | echo "Extracting ${url} to ${temp[m86]}" 48 | net_extract "${url}" "${temp[m86]}" 49 | 50 | stderr=$(fatcat -O 1048576 "${temp[m86]}/memtest86-usb.img" \ 51 | -r /EFI/BOOT/BOOTX64.efi 2>&1 > "${temp[m86]}/bootx64.efi") 52 | 53 | if [[ -n "${stderr}" ]]; then 54 | echo "Error when extract memtest86 image:" 55 | echo "${stderr}" 56 | else 57 | mv "${temp[m86]}/bootx64.efi" "${target}" 58 | fi 59 | fi 60 | fi 61 | 62 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 63 | -------------------------------------------------------------------------------- /prepare.d/freebsd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #root: freebsd 3 | 4 | source functions.sh 5 | cd "${LOCAL_PATH}" || exit 1 6 | 7 | count=0 8 | while read -r ver; do 9 | for arch in amd64 i386; do 10 | url="${FREEBSD_MIRROR}/releases/ISO-IMAGES/${ver}/FreeBSD-${ver}-RELEASE-${arch}-disc1.iso" 11 | url_check "${url}" || continue 12 | target="${ver}_${arch}" 13 | 14 | if [[ -d "${target}" ]]; then 15 | echo "Already exists: ${target}" 16 | else 17 | echo "Extracting ${url} to ${target}" 18 | 19 | id="freebsd_${RANDOM}" 20 | temp[${id}]=".tmp.${target}" 21 | ( 22 | set -eo pipefail 23 | 24 | relpath=$(realpath --relative-to="${PXE_LOCAL_ROOT}" "${target}") 25 | mkdir "${temp[${id}]}" && cd "${temp[${id}]}" 26 | 27 | curl -fsL "${url}" -o "cd.iso" 28 | bsdtar -x -f "cd.iso" --exclude='usr/' && mkdir usr 29 | 30 | efi_loader="boot/loader.efi" 31 | pxe_loader="boot/loader_pxe.efi" 32 | install -m644 "${efi_loader}" "${pxe_loader}" 33 | 34 | true > etc/fstab 35 | cat >> etc/rc.initdiskless <<- EOF 36 | mount -t tmpfs tmpfs /mnt 37 | mkdir /mnt/iso 38 | mount -t cd9660 /dev/\`mdconfig -f /cd.iso\` /mnt/iso 39 | for path in bin lib libexec root sbin usr; do 40 | mount -t nullfs /mnt/iso/\$path /\$path 41 | done 42 | for path in etc var; do 43 | eval md_size_\$path=32768 44 | create_md \$path 45 | cp -Rp /mnt/iso/\$path/ /\$path 46 | done 47 | true > /etc/fstab 48 | EOF 49 | 50 | mapfile -t loc < <(grep -obUaP "\x2f\x00{127}" "${efi_loader}" | cut -d: -f1) 51 | (( ${#loc[@]} == 1 )) 52 | 53 | printf '%s\x00' "${PXE_NFS_ROOT}/${relpath}" \ 54 | | dd of="${pxe_loader}" bs=1 seek="${loc[0]}" conv=notrunc 55 | ) 56 | [[ $? == 0 ]] && mv "${temp[${id}]}" "${target}" 57 | fi 58 | done 59 | 60 | count=$((count+1)) 61 | [[ "${count}" -ge 2 ]] && break 62 | done < <(svn ls https://svn.freebsd.org/base/releng/ | grep -Po '^\d+\.\d+(?=/$)' | sort -Vr) 63 | 64 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 65 | -------------------------------------------------------------------------------- /prepare.d/netbsd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #root: netbsd 3 | 4 | source functions.sh 5 | cd "${LOCAL_PATH}" || exit 1 6 | 7 | last_major_ver=100 8 | while read -r ver; do 9 | major_ver=${ver%%.*} 10 | 11 | #(( major_ver < 8 )) && break 12 | (( major_ver < last_major_ver )) \ 13 | && last_major_ver="${major_ver}" \ 14 | || continue 15 | 16 | for arch in amd64 i386; do 17 | url="${NETBSD_MIRROR}/images/${ver}/NetBSD-${ver}-${arch}.iso" 18 | url_check "${url}" || continue 19 | target="${ver}_${arch}" 20 | 21 | if [[ -d "${target}" ]]; then 22 | echo "Already exists: ${target}" 23 | else 24 | echo "Extracting ${url} to ${target}" 25 | 26 | id="netbsd_${RANDOM}" 27 | temp[${id}]=".tmp.${target}" 28 | ( 29 | set -eo pipefail 30 | 31 | relpath=$(realpath --relative-to="${PXE_LOCAL_ROOT}" "${target}") 32 | mkdir "${temp[${id}]}" && cd "${temp[${id}]}" 33 | curl -fsL "${url}" -o "cd.iso" 34 | 35 | # Generate miniroot 36 | mkdir miniroot 37 | bsdtar -C miniroot -xf cd.iso \ 38 | "netbsd" "${arch}/" "dev/MAKEDEV" 39 | 40 | if [[ -f "miniroot/${arch}/binary/sets/base.tgz" ]]; then 41 | bsdtar -C miniroot -xf "miniroot/${arch}/binary/sets/base.tgz" rescue/ 42 | elif [[ -f "miniroot/${arch}/binary/sets/base.tar.xz" ]]; then 43 | bsdtar -C miniroot -Jxf "miniroot/${arch}/binary/sets/base.tar.xz" rescue/ 44 | else 45 | echo 'Neither base.{tgz,tar.xz} is found' >&2 46 | fi 47 | 48 | mkdir miniroot/{etc,tmp,var} 49 | mv "miniroot/${arch}/binary/kernel/netbsd-INSTALL.gz" . 50 | mv "miniroot/netbsd" . 51 | 52 | zcat "miniroot/${arch}/installation/miniroot/miniroot.kmod" > miniroot.kmod 53 | rm -rf "miniroot/${arch}" 54 | objcopy miniroot.kmod --dump-section miniroot=miniroot/miniroot.fs 55 | bzip2 -k --best miniroot/miniroot.fs 56 | 57 | echo "nfs_path=\"${PXE_NFS_HOST}:${PXE_NFS_ROOT}/${relpath}\"" >> miniroot/etc/rc 58 | sed -e 's/#.*$//' -e '/^$/d' "${ASSET_PATH}/rc.miniroot" >> miniroot/etc/rc 59 | 60 | genisoimage -o miniroot.iso -JR miniroot/ 61 | objcopy --update-section miniroot=miniroot.iso miniroot.kmod 62 | gzip --best miniroot.kmod 63 | 64 | rm miniroot.iso 65 | rm -rf miniroot/ 66 | ) 67 | [[ $? == 0 ]] && mv "${temp[${id}]}" "${target}" 68 | fi 69 | done 70 | done < <(grep_web "${NETBSD_MIRROR}/README" '^NetBSD-\K[0-9.]+') 71 | 72 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 73 | -------------------------------------------------------------------------------- /functions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | declare -Ag temp 4 | cleanup_temp() { 5 | for key in "${!temp[@]}"; do 6 | echo "Clean up: ${temp[${key}]}" 7 | rm -rf -- "${temp[${key}]}" 8 | done 9 | } 10 | trap 'cleanup_temp' EXIT 11 | 12 | panick() { 13 | local mesg str 14 | mesg=$1 && shift 15 | str=$(printf "==> ERROR: ${mesg}\n" "$@") 16 | echo "${str}" >&2 17 | exit 1 18 | } 19 | 20 | url2grub() { 21 | local url="$1" 22 | local url_regex='^(http|tftp)://([^/]+)(/.*)?$' 23 | if [[ "${url}" =~ ${url_regex} ]]; then 24 | local protocol="${BASH_REMATCH[1]}" 25 | local host="${BASH_REMATCH[2]}" 26 | local path="${BASH_REMATCH[3]}" 27 | echo "(${protocol},${host})${path}" 28 | fi 29 | } 30 | 31 | net_extract() { 32 | local url target workdir 33 | url="$1" 34 | target="$2" 35 | workdir=$(dirname "${target}") 36 | mkdir -p "${workdir}" 37 | 38 | id="net_extract_${RANDOM}" 39 | temp[${id}]=$(mktemp -d -p "${workdir}" .tmp.XXXXXXXX) || return 1 40 | 41 | curl -fsL "${url}" | bsdtar -x -f - -C "${temp[${id}]}" 42 | 43 | if [[ "${PIPESTATUS[0]}" == 0 && "$?" == 0 ]]; then 44 | chmod 755 "${temp[${id}]}" 45 | [[ -d "${target}" ]] && mv "${target}" "${target}.bak" 46 | mv "${temp[${id}]}" "${target}" 47 | else 48 | echo "Download failed" 49 | rm -rf "${temp[${id}]}" 50 | return 1 51 | fi 52 | } 53 | 54 | url_check() { 55 | local url code 56 | 57 | url="$1" && shift 58 | code=$(curl -sIL -o /dev/null -w "%{http_code}" "${url}") || return 1 59 | 60 | (( code < 400 )) && return 0 61 | for white in "$@"; do 62 | (( code == white )) && return 0 63 | done 64 | 65 | return 1 66 | } 67 | 68 | grep_web() { 69 | local url="$1" 70 | local regex="$2" 71 | curl -fsL "${url}" | grep -Po "${regex}" | sort -uVr 72 | [[ "${PIPESTATUS[0]}" == 0 ]] 73 | } 74 | 75 | grub_menu_sep() { 76 | cat <<-EOF 77 | menuentry '$1' { 78 | true 79 | } 80 | EOF 81 | } 82 | 83 | grub_linux_entry() { 84 | local title="$1" 85 | local kernel="$2" 86 | local initrd="$3" 87 | local param="$4" 88 | cat <<-EOF 89 | menuentry '${title}' { 90 | echo 'Loading kernel...' 91 | linux ${kernel} ${param} 92 | echo 'Loading initial ramdisk...' 93 | initrd ${initrd} 94 | } 95 | EOF 96 | } 97 | 98 | grub_option() { 99 | local var desc index title 100 | 101 | var="$1" && shift 102 | desc="$1" && shift 103 | 104 | cat <<- EOF 105 | if [ -z "\$${var}_INDEX" ]; then 106 | set ${var}='$1' 107 | set ${var}_DNAME='$2' 108 | set ${var}_INDEX=0 109 | fi 110 | 111 | submenu "[option] ${desc} = \$${var}_DNAME" { 112 | menuentry '${desc}:' { 113 | export ${var} 114 | export ${var}_DNAME 115 | export ${var}_INDEX 116 | configfile ${PXE_MENU_URL} 117 | } 118 | EOF 119 | 120 | index=0 121 | while (("$#" >= 3)); do 122 | title="> $2" 123 | [[ -n "$3" ]] && title="${title}: $3" 124 | 125 | cat <<-EOF 126 | menuentry '${title}' { 127 | set ${var}='$1' 128 | set ${var}_DNAME='$2' 129 | set ${var}_INDEX=${index} 130 | export ${var} 131 | export ${var}_DNAME 132 | export ${var}_INDEX 133 | configfile ${PXE_MENU_URL} 134 | } 135 | EOF 136 | 137 | shift 3 138 | index=$((index+1)) 139 | done 140 | 141 | echo "}" 142 | } 143 | 144 | grub_mirror_selector() { 145 | local grub_var="$1" && shift 146 | 147 | declare -a buf 148 | for url in "$@"; do 149 | buf=("${buf[@]}" "${url}" "${url}" "") 150 | done 151 | 152 | grub_option "${grub_var}" "Mirror URL" "${buf[@]}" 153 | 154 | cat <<-EOF 155 | regexp \\ 156 | --set=1:mirror_protocol \\ 157 | --set=2:mirror_host \\ 158 | --set=3:mirror_path \\ 159 | '^([^:]+)://([^/]+)(/.*)\$' "\$${grub_var}" 160 | EOF 161 | } 162 | 163 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 164 | -------------------------------------------------------------------------------- /simple-pxe: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source functions.sh 4 | 5 | export {ARCHLINUX,ROCKY,DEBIAN_CD,DEBIAN,FEDORA,FREEBSD,NETBSD,UBUNTU,UBUNTU_RELEASES}_MIRROR 6 | export {ARCHLINUX,ROCKY,DEBIAN_CD,DEBIAN,FEDORA,FREEBSD,NETBSD,UBUNTU,UBUNTU_RELEASES}_MIRROR_BACKUP 7 | 8 | mirror_base="http://mirrors.ustc.edu.cn" 9 | ARCHLINUX_MIRROR="${mirror_base}/archlinux" 10 | ROCKY_MIRROR="${mirror_base}/rocky" 11 | DEBIAN_CD_MIRROR="${mirror_base}/debian-cd" 12 | DEBIAN_MIRROR="${mirror_base}/debian" 13 | FEDORA_MIRROR="${mirror_base}/fedora" 14 | FREEBSD_MIRROR="${mirror_base}/freebsd" 15 | NETBSD_MIRROR="https://mirrors.bfsu.edu.cn/NetBSD" 16 | UBUNTU_MIRROR="${mirror_base}/ubuntu" 17 | UBUNTU_RELEASES_MIRROR="${mirror_base}/ubuntu-releases" 18 | 19 | export PXE_LOCAL_ROOT PXE_HTTP_ROOT PXE_NFS_HOST PXE_NFS_ROOT 20 | declare -a PXE_PREPARE PXE_MENUS 21 | source config.local 22 | 23 | validate_config() { 24 | [[ -d "${PXE_LOCAL_ROOT}" ]] || panick "\$PXE_LOCAL_ROOT is not a valid directory" 25 | [[ -n "${PXE_HTTP_ROOT}" ]] || panick "Please set \$PXE_HTTP_ROOT" 26 | [[ -n "${PXE_NFS_HOST}" ]] || panick "Please set \$PXE_NFS_HOST" 27 | [[ -n "${PXE_NFS_ROOT}" ]] || panick "Please set \$PXE_NFS_ROOT" 28 | } 29 | 30 | parse_header() { 31 | # PXE_LOCAL_ROOT -> LOCAL_PATH 32 | # PXE_HTTP_ROOT -> HTTP_PATH 33 | # GRUB_PATH 34 | # PXE_NFS_ROOT -> NFS_PATH 35 | 36 | unset header && declare -Ag header 37 | local fullpath filename key value 38 | 39 | fullpath="$1" 40 | filename=$(basename -- "${fullpath}") 41 | filename="${filename%.*}" 42 | 43 | while read -r line; do 44 | if [[ "${line}" =~ ^\#([[:alnum:]]+):[[:space:]]*(.*)$ ]]; then 45 | key="${BASH_REMATCH[1]}" 46 | value="${BASH_REMATCH[2]}" 47 | header["${key}"]="${value}" 48 | fi 49 | done < "${fullpath}" 50 | 51 | [[ -z "${header[root]}" ]] && header[root]="${filename}" 52 | 53 | ASSET_PATH=$(readlink -m "$(dirname "${fullpath}")/${filename}") 54 | LOCAL_PATH="${PXE_LOCAL_ROOT}/${header[root]}" 55 | HTTP_PATH="${PXE_HTTP_ROOT}/${header[root]}" 56 | GRUB_PATH=$(url2grub "${HTTP_PATH}") 57 | NFS_PATH="${PXE_NFS_ROOT}/${header[root]}" 58 | export ASSET_PATH LOCAL_PATH HTTP_PATH GRUB_PATH NFS_PATH 59 | } 60 | 61 | do_prepare() { 62 | for item in "${PXE_PREPARE[@]}"; do 63 | script="prepare.d/${item}.sh" 64 | 65 | if [[ -f "${script}" ]]; then 66 | echo "Run ${script}..." 67 | parse_header "${script}" 68 | mkdir -p "${LOCAL_PATH}" 69 | bash "${script}" 70 | fi 71 | done 72 | 73 | echo "Prepare OK" 74 | } 75 | 76 | do_menu() { 77 | menu_dir="${PXE_LOCAL_ROOT}/menu" 78 | menu_url=$(url2grub "${PXE_HTTP_ROOT}/menu") 79 | mkdir -p "${menu_dir}" 80 | 81 | menu_root="${menu_dir}/root.menu" 82 | temp[menu_root]=$(mktemp) 83 | 84 | intro_flag=0 85 | if [[ -n "${PXE_INTRO}" ]]; then 86 | echo "${PXE_INTRO}" > "${PXE_LOCAL_ROOT}/intro.txt" 87 | intro_flag=1 88 | fi 89 | 90 | cat >> "${temp[menu_root]}" <<- EOF 91 | set menu_color_highlight=${GRUB_COLOR_HIGHLIGHT:-white/red} 92 | set menu_color_normal=${GRUB_COLOR_NORMAL:-white/blue} 93 | export menu_color_highlight 94 | export menu_color_normal 95 | 96 | if [ "\$feature_all_video_module" = "y" ]; then 97 | insmod all_video 98 | else 99 | for mod in efi_gop efi_uga ieee1275_fb vbe vga video_bochs video_cirrus; do 100 | insmod \$mod 101 | done 102 | fi 103 | 104 | menuentry '${PXE_TITLE:-Network Boot Service}' { 105 | if [ ${intro_flag} = 1 ]; then 106 | set pager=1 107 | cat $(url2grub "${PXE_HTTP_ROOT}/intro.txt") 108 | echo "Press any key to continue" 109 | read 110 | fi 111 | } 112 | EOF 113 | 114 | for item in "${PXE_MENUS[@]}"; do 115 | if [[ ${item:0:1} == "-" ]]; then 116 | grub_menu_sep "<${item:1}>" >> "${temp[menu_root]}" 117 | continue 118 | fi 119 | 120 | local script="menu.d/${item}.sh" 121 | local menu_file="${menu_dir}/${item}.menu" 122 | export PXE_MENU_URL="${menu_url}/${item}.menu" 123 | 124 | if [[ -f "${script}" ]]; then 125 | parse_header "${script}" 126 | echo "Generating menu item '${header[menu]}'..." 127 | 128 | id="do_menu_${item}" 129 | temp[${id}]=$(mktemp) 130 | 131 | cat >> "${temp[${id}]}" <<- EOF 132 | menuentry '..' { 133 | configfile "${menu_url}/root.menu" 134 | } 135 | EOF 136 | 137 | if bash "${script}" >> "${temp[${id}]}"; then 138 | chmod 644 "${temp[${id}]}" 139 | mv "${temp[${id}]}" "${menu_file}" 140 | else 141 | echo "${item}: failed to generate menu" 142 | fi 143 | fi 144 | 145 | if [[ -f "${menu_file}" ]]; then 146 | cat >> "${temp[menu_root]}" <<- EOF 147 | menuentry '- ${header[menu]}' { 148 | configfile "${PXE_MENU_URL}" 149 | } 150 | EOF 151 | fi 152 | done 153 | 154 | chmod 644 "${temp[menu_root]}" 155 | mv "${temp[menu_root]}" "${menu_root}" 156 | } 157 | 158 | # main {{{ 159 | readonly ACTION="${1:-menu}" 160 | validate_config 161 | 162 | case "${ACTION}" in 163 | prepare) 164 | do_prepare 165 | ;; 166 | menu) 167 | do_menu 168 | ;; 169 | *) 170 | panick "Unknown action: ${ACTION}" 171 | ;; 172 | esac 173 | # }}} 174 | 175 | # vim: set ts=4 sw=4 sts=4 noexpandtab nosta: 176 | -------------------------------------------------------------------------------- /grub.example/grub.cfg: -------------------------------------------------------------------------------- 1 | function dec_to_hex { 2 | if [ "$1" -eq 0 ]; then 3 | set result=00 4 | elif [ "$1" -eq 1 ]; then 5 | set result=01 6 | elif [ "$1" -eq 2 ]; then 7 | set result=02 8 | elif [ "$1" -eq 3 ]; then 9 | set result=03 10 | elif [ "$1" -eq 4 ]; then 11 | set result=04 12 | elif [ "$1" -eq 5 ]; then 13 | set result=05 14 | elif [ "$1" -eq 6 ]; then 15 | set result=06 16 | elif [ "$1" -eq 7 ]; then 17 | set result=07 18 | elif [ "$1" -eq 8 ]; then 19 | set result=08 20 | elif [ "$1" -eq 9 ]; then 21 | set result=09 22 | elif [ "$1" -eq 10 ]; then 23 | set result=0A 24 | elif [ "$1" -eq 11 ]; then 25 | set result=0B 26 | elif [ "$1" -eq 12 ]; then 27 | set result=0C 28 | elif [ "$1" -eq 13 ]; then 29 | set result=0D 30 | elif [ "$1" -eq 14 ]; then 31 | set result=0E 32 | elif [ "$1" -eq 15 ]; then 33 | set result=0F 34 | elif [ "$1" -eq 16 ]; then 35 | set result=10 36 | elif [ "$1" -eq 17 ]; then 37 | set result=11 38 | elif [ "$1" -eq 18 ]; then 39 | set result=12 40 | elif [ "$1" -eq 19 ]; then 41 | set result=13 42 | elif [ "$1" -eq 20 ]; then 43 | set result=14 44 | elif [ "$1" -eq 21 ]; then 45 | set result=15 46 | elif [ "$1" -eq 22 ]; then 47 | set result=16 48 | elif [ "$1" -eq 23 ]; then 49 | set result=17 50 | elif [ "$1" -eq 24 ]; then 51 | set result=18 52 | elif [ "$1" -eq 25 ]; then 53 | set result=19 54 | elif [ "$1" -eq 26 ]; then 55 | set result=1A 56 | elif [ "$1" -eq 27 ]; then 57 | set result=1B 58 | elif [ "$1" -eq 28 ]; then 59 | set result=1C 60 | elif [ "$1" -eq 29 ]; then 61 | set result=1D 62 | elif [ "$1" -eq 30 ]; then 63 | set result=1E 64 | elif [ "$1" -eq 31 ]; then 65 | set result=1F 66 | elif [ "$1" -eq 32 ]; then 67 | set result=20 68 | elif [ "$1" -eq 33 ]; then 69 | set result=21 70 | elif [ "$1" -eq 34 ]; then 71 | set result=22 72 | elif [ "$1" -eq 35 ]; then 73 | set result=23 74 | elif [ "$1" -eq 36 ]; then 75 | set result=24 76 | elif [ "$1" -eq 37 ]; then 77 | set result=25 78 | elif [ "$1" -eq 38 ]; then 79 | set result=26 80 | elif [ "$1" -eq 39 ]; then 81 | set result=27 82 | elif [ "$1" -eq 40 ]; then 83 | set result=28 84 | elif [ "$1" -eq 41 ]; then 85 | set result=29 86 | elif [ "$1" -eq 42 ]; then 87 | set result=2A 88 | elif [ "$1" -eq 43 ]; then 89 | set result=2B 90 | elif [ "$1" -eq 44 ]; then 91 | set result=2C 92 | elif [ "$1" -eq 45 ]; then 93 | set result=2D 94 | elif [ "$1" -eq 46 ]; then 95 | set result=2E 96 | elif [ "$1" -eq 47 ]; then 97 | set result=2F 98 | elif [ "$1" -eq 48 ]; then 99 | set result=30 100 | elif [ "$1" -eq 49 ]; then 101 | set result=31 102 | elif [ "$1" -eq 50 ]; then 103 | set result=32 104 | elif [ "$1" -eq 51 ]; then 105 | set result=33 106 | elif [ "$1" -eq 52 ]; then 107 | set result=34 108 | elif [ "$1" -eq 53 ]; then 109 | set result=35 110 | elif [ "$1" -eq 54 ]; then 111 | set result=36 112 | elif [ "$1" -eq 55 ]; then 113 | set result=37 114 | elif [ "$1" -eq 56 ]; then 115 | set result=38 116 | elif [ "$1" -eq 57 ]; then 117 | set result=39 118 | elif [ "$1" -eq 58 ]; then 119 | set result=3A 120 | elif [ "$1" -eq 59 ]; then 121 | set result=3B 122 | elif [ "$1" -eq 60 ]; then 123 | set result=3C 124 | elif [ "$1" -eq 61 ]; then 125 | set result=3D 126 | elif [ "$1" -eq 62 ]; then 127 | set result=3E 128 | elif [ "$1" -eq 63 ]; then 129 | set result=3F 130 | elif [ "$1" -eq 64 ]; then 131 | set result=40 132 | elif [ "$1" -eq 65 ]; then 133 | set result=41 134 | elif [ "$1" -eq 66 ]; then 135 | set result=42 136 | elif [ "$1" -eq 67 ]; then 137 | set result=43 138 | elif [ "$1" -eq 68 ]; then 139 | set result=44 140 | elif [ "$1" -eq 69 ]; then 141 | set result=45 142 | elif [ "$1" -eq 70 ]; then 143 | set result=46 144 | elif [ "$1" -eq 71 ]; then 145 | set result=47 146 | elif [ "$1" -eq 72 ]; then 147 | set result=48 148 | elif [ "$1" -eq 73 ]; then 149 | set result=49 150 | elif [ "$1" -eq 74 ]; then 151 | set result=4A 152 | elif [ "$1" -eq 75 ]; then 153 | set result=4B 154 | elif [ "$1" -eq 76 ]; then 155 | set result=4C 156 | elif [ "$1" -eq 77 ]; then 157 | set result=4D 158 | elif [ "$1" -eq 78 ]; then 159 | set result=4E 160 | elif [ "$1" -eq 79 ]; then 161 | set result=4F 162 | elif [ "$1" -eq 80 ]; then 163 | set result=50 164 | elif [ "$1" -eq 81 ]; then 165 | set result=51 166 | elif [ "$1" -eq 82 ]; then 167 | set result=52 168 | elif [ "$1" -eq 83 ]; then 169 | set result=53 170 | elif [ "$1" -eq 84 ]; then 171 | set result=54 172 | elif [ "$1" -eq 85 ]; then 173 | set result=55 174 | elif [ "$1" -eq 86 ]; then 175 | set result=56 176 | elif [ "$1" -eq 87 ]; then 177 | set result=57 178 | elif [ "$1" -eq 88 ]; then 179 | set result=58 180 | elif [ "$1" -eq 89 ]; then 181 | set result=59 182 | elif [ "$1" -eq 90 ]; then 183 | set result=5A 184 | elif [ "$1" -eq 91 ]; then 185 | set result=5B 186 | elif [ "$1" -eq 92 ]; then 187 | set result=5C 188 | elif [ "$1" -eq 93 ]; then 189 | set result=5D 190 | elif [ "$1" -eq 94 ]; then 191 | set result=5E 192 | elif [ "$1" -eq 95 ]; then 193 | set result=5F 194 | elif [ "$1" -eq 96 ]; then 195 | set result=60 196 | elif [ "$1" -eq 97 ]; then 197 | set result=61 198 | elif [ "$1" -eq 98 ]; then 199 | set result=62 200 | elif [ "$1" -eq 99 ]; then 201 | set result=63 202 | elif [ "$1" -eq 100 ]; then 203 | set result=64 204 | elif [ "$1" -eq 101 ]; then 205 | set result=65 206 | elif [ "$1" -eq 102 ]; then 207 | set result=66 208 | elif [ "$1" -eq 103 ]; then 209 | set result=67 210 | elif [ "$1" -eq 104 ]; then 211 | set result=68 212 | elif [ "$1" -eq 105 ]; then 213 | set result=69 214 | elif [ "$1" -eq 106 ]; then 215 | set result=6A 216 | elif [ "$1" -eq 107 ]; then 217 | set result=6B 218 | elif [ "$1" -eq 108 ]; then 219 | set result=6C 220 | elif [ "$1" -eq 109 ]; then 221 | set result=6D 222 | elif [ "$1" -eq 110 ]; then 223 | set result=6E 224 | elif [ "$1" -eq 111 ]; then 225 | set result=6F 226 | elif [ "$1" -eq 112 ]; then 227 | set result=70 228 | elif [ "$1" -eq 113 ]; then 229 | set result=71 230 | elif [ "$1" -eq 114 ]; then 231 | set result=72 232 | elif [ "$1" -eq 115 ]; then 233 | set result=73 234 | elif [ "$1" -eq 116 ]; then 235 | set result=74 236 | elif [ "$1" -eq 117 ]; then 237 | set result=75 238 | elif [ "$1" -eq 118 ]; then 239 | set result=76 240 | elif [ "$1" -eq 119 ]; then 241 | set result=77 242 | elif [ "$1" -eq 120 ]; then 243 | set result=78 244 | elif [ "$1" -eq 121 ]; then 245 | set result=79 246 | elif [ "$1" -eq 122 ]; then 247 | set result=7A 248 | elif [ "$1" -eq 123 ]; then 249 | set result=7B 250 | elif [ "$1" -eq 124 ]; then 251 | set result=7C 252 | elif [ "$1" -eq 125 ]; then 253 | set result=7D 254 | elif [ "$1" -eq 126 ]; then 255 | set result=7E 256 | elif [ "$1" -eq 127 ]; then 257 | set result=7F 258 | elif [ "$1" -eq 128 ]; then 259 | set result=80 260 | elif [ "$1" -eq 129 ]; then 261 | set result=81 262 | elif [ "$1" -eq 130 ]; then 263 | set result=82 264 | elif [ "$1" -eq 131 ]; then 265 | set result=83 266 | elif [ "$1" -eq 132 ]; then 267 | set result=84 268 | elif [ "$1" -eq 133 ]; then 269 | set result=85 270 | elif [ "$1" -eq 134 ]; then 271 | set result=86 272 | elif [ "$1" -eq 135 ]; then 273 | set result=87 274 | elif [ "$1" -eq 136 ]; then 275 | set result=88 276 | elif [ "$1" -eq 137 ]; then 277 | set result=89 278 | elif [ "$1" -eq 138 ]; then 279 | set result=8A 280 | elif [ "$1" -eq 139 ]; then 281 | set result=8B 282 | elif [ "$1" -eq 140 ]; then 283 | set result=8C 284 | elif [ "$1" -eq 141 ]; then 285 | set result=8D 286 | elif [ "$1" -eq 142 ]; then 287 | set result=8E 288 | elif [ "$1" -eq 143 ]; then 289 | set result=8F 290 | elif [ "$1" -eq 144 ]; then 291 | set result=90 292 | elif [ "$1" -eq 145 ]; then 293 | set result=91 294 | elif [ "$1" -eq 146 ]; then 295 | set result=92 296 | elif [ "$1" -eq 147 ]; then 297 | set result=93 298 | elif [ "$1" -eq 148 ]; then 299 | set result=94 300 | elif [ "$1" -eq 149 ]; then 301 | set result=95 302 | elif [ "$1" -eq 150 ]; then 303 | set result=96 304 | elif [ "$1" -eq 151 ]; then 305 | set result=97 306 | elif [ "$1" -eq 152 ]; then 307 | set result=98 308 | elif [ "$1" -eq 153 ]; then 309 | set result=99 310 | elif [ "$1" -eq 154 ]; then 311 | set result=9A 312 | elif [ "$1" -eq 155 ]; then 313 | set result=9B 314 | elif [ "$1" -eq 156 ]; then 315 | set result=9C 316 | elif [ "$1" -eq 157 ]; then 317 | set result=9D 318 | elif [ "$1" -eq 158 ]; then 319 | set result=9E 320 | elif [ "$1" -eq 159 ]; then 321 | set result=9F 322 | elif [ "$1" -eq 160 ]; then 323 | set result=A0 324 | elif [ "$1" -eq 161 ]; then 325 | set result=A1 326 | elif [ "$1" -eq 162 ]; then 327 | set result=A2 328 | elif [ "$1" -eq 163 ]; then 329 | set result=A3 330 | elif [ "$1" -eq 164 ]; then 331 | set result=A4 332 | elif [ "$1" -eq 165 ]; then 333 | set result=A5 334 | elif [ "$1" -eq 166 ]; then 335 | set result=A6 336 | elif [ "$1" -eq 167 ]; then 337 | set result=A7 338 | elif [ "$1" -eq 168 ]; then 339 | set result=A8 340 | elif [ "$1" -eq 169 ]; then 341 | set result=A9 342 | elif [ "$1" -eq 170 ]; then 343 | set result=AA 344 | elif [ "$1" -eq 171 ]; then 345 | set result=AB 346 | elif [ "$1" -eq 172 ]; then 347 | set result=AC 348 | elif [ "$1" -eq 173 ]; then 349 | set result=AD 350 | elif [ "$1" -eq 174 ]; then 351 | set result=AE 352 | elif [ "$1" -eq 175 ]; then 353 | set result=AF 354 | elif [ "$1" -eq 176 ]; then 355 | set result=B0 356 | elif [ "$1" -eq 177 ]; then 357 | set result=B1 358 | elif [ "$1" -eq 178 ]; then 359 | set result=B2 360 | elif [ "$1" -eq 179 ]; then 361 | set result=B3 362 | elif [ "$1" -eq 180 ]; then 363 | set result=B4 364 | elif [ "$1" -eq 181 ]; then 365 | set result=B5 366 | elif [ "$1" -eq 182 ]; then 367 | set result=B6 368 | elif [ "$1" -eq 183 ]; then 369 | set result=B7 370 | elif [ "$1" -eq 184 ]; then 371 | set result=B8 372 | elif [ "$1" -eq 185 ]; then 373 | set result=B9 374 | elif [ "$1" -eq 186 ]; then 375 | set result=BA 376 | elif [ "$1" -eq 187 ]; then 377 | set result=BB 378 | elif [ "$1" -eq 188 ]; then 379 | set result=BC 380 | elif [ "$1" -eq 189 ]; then 381 | set result=BD 382 | elif [ "$1" -eq 190 ]; then 383 | set result=BE 384 | elif [ "$1" -eq 191 ]; then 385 | set result=BF 386 | elif [ "$1" -eq 192 ]; then 387 | set result=C0 388 | elif [ "$1" -eq 193 ]; then 389 | set result=C1 390 | elif [ "$1" -eq 194 ]; then 391 | set result=C2 392 | elif [ "$1" -eq 195 ]; then 393 | set result=C3 394 | elif [ "$1" -eq 196 ]; then 395 | set result=C4 396 | elif [ "$1" -eq 197 ]; then 397 | set result=C5 398 | elif [ "$1" -eq 198 ]; then 399 | set result=C6 400 | elif [ "$1" -eq 199 ]; then 401 | set result=C7 402 | elif [ "$1" -eq 200 ]; then 403 | set result=C8 404 | elif [ "$1" -eq 201 ]; then 405 | set result=C9 406 | elif [ "$1" -eq 202 ]; then 407 | set result=CA 408 | elif [ "$1" -eq 203 ]; then 409 | set result=CB 410 | elif [ "$1" -eq 204 ]; then 411 | set result=CC 412 | elif [ "$1" -eq 205 ]; then 413 | set result=CD 414 | elif [ "$1" -eq 206 ]; then 415 | set result=CE 416 | elif [ "$1" -eq 207 ]; then 417 | set result=CF 418 | elif [ "$1" -eq 208 ]; then 419 | set result=D0 420 | elif [ "$1" -eq 209 ]; then 421 | set result=D1 422 | elif [ "$1" -eq 210 ]; then 423 | set result=D2 424 | elif [ "$1" -eq 211 ]; then 425 | set result=D3 426 | elif [ "$1" -eq 212 ]; then 427 | set result=D4 428 | elif [ "$1" -eq 213 ]; then 429 | set result=D5 430 | elif [ "$1" -eq 214 ]; then 431 | set result=D6 432 | elif [ "$1" -eq 215 ]; then 433 | set result=D7 434 | elif [ "$1" -eq 216 ]; then 435 | set result=D8 436 | elif [ "$1" -eq 217 ]; then 437 | set result=D9 438 | elif [ "$1" -eq 218 ]; then 439 | set result=DA 440 | elif [ "$1" -eq 219 ]; then 441 | set result=DB 442 | elif [ "$1" -eq 220 ]; then 443 | set result=DC 444 | elif [ "$1" -eq 221 ]; then 445 | set result=DD 446 | elif [ "$1" -eq 222 ]; then 447 | set result=DE 448 | elif [ "$1" -eq 223 ]; then 449 | set result=DF 450 | elif [ "$1" -eq 224 ]; then 451 | set result=E0 452 | elif [ "$1" -eq 225 ]; then 453 | set result=E1 454 | elif [ "$1" -eq 226 ]; then 455 | set result=E2 456 | elif [ "$1" -eq 227 ]; then 457 | set result=E3 458 | elif [ "$1" -eq 228 ]; then 459 | set result=E4 460 | elif [ "$1" -eq 229 ]; then 461 | set result=E5 462 | elif [ "$1" -eq 230 ]; then 463 | set result=E6 464 | elif [ "$1" -eq 231 ]; then 465 | set result=E7 466 | elif [ "$1" -eq 232 ]; then 467 | set result=E8 468 | elif [ "$1" -eq 233 ]; then 469 | set result=E9 470 | elif [ "$1" -eq 234 ]; then 471 | set result=EA 472 | elif [ "$1" -eq 235 ]; then 473 | set result=EB 474 | elif [ "$1" -eq 236 ]; then 475 | set result=EC 476 | elif [ "$1" -eq 237 ]; then 477 | set result=ED 478 | elif [ "$1" -eq 238 ]; then 479 | set result=EE 480 | elif [ "$1" -eq 239 ]; then 481 | set result=EF 482 | elif [ "$1" -eq 240 ]; then 483 | set result=F0 484 | elif [ "$1" -eq 241 ]; then 485 | set result=F1 486 | elif [ "$1" -eq 242 ]; then 487 | set result=F2 488 | elif [ "$1" -eq 243 ]; then 489 | set result=F3 490 | elif [ "$1" -eq 244 ]; then 491 | set result=F4 492 | elif [ "$1" -eq 245 ]; then 493 | set result=F5 494 | elif [ "$1" -eq 246 ]; then 495 | set result=F6 496 | elif [ "$1" -eq 247 ]; then 497 | set result=F7 498 | elif [ "$1" -eq 248 ]; then 499 | set result=F8 500 | elif [ "$1" -eq 249 ]; then 501 | set result=F9 502 | elif [ "$1" -eq 250 ]; then 503 | set result=FA 504 | elif [ "$1" -eq 251 ]; then 505 | set result=FB 506 | elif [ "$1" -eq 252 ]; then 507 | set result=FC 508 | elif [ "$1" -eq 253 ]; then 509 | set result=FD 510 | elif [ "$1" -eq 254 ]; then 511 | set result=FE 512 | elif [ "$1" -eq 255 ]; then 513 | set result=FF 514 | else 515 | set result="" 516 | fi 517 | } 518 | 519 | set mac_addr="${net_pxe_mac}" 520 | if [ -z "${net_pxe_mac}" ]; then 521 | set mac_addr="${net_default_mac}" 522 | fi 523 | 524 | set ip_addr="${net_pxe_ip}" 525 | if [ -z "${net_pxe_ip}" ]; then 526 | set ip_addr="${net_default_ip}" 527 | fi 528 | 529 | grub_cfg_dir="${prefix}/grub.cfg.d" 530 | 531 | if [ -n "${mac_addr}" ]; then 532 | configfile "${grub_cfg_dir}/${mac_addr}" 533 | fi 534 | 535 | if [ -n "${ip_addr}" ]; then 536 | set regex_buf='^' 537 | set ip_addr_hex="" 538 | for index in 1 2 3 4; do 539 | regexp --set=num_dec "${regex_buf}([[:digit:]]+)" "${ip_addr}" 540 | dec_to_hex "${num_dec}" 541 | set ip_addr_hex="${ip_addr_hex}${result}" 542 | set regex_buf="${regex_buf}[[:digit:]]+\." 543 | done 544 | 545 | for index in 8 7 6 5 4 3 2 1; do 546 | regexp --set=regex_match "^([[:xdigit:]]{${index}})" "${ip_addr_hex}" 547 | configfile "${grub_cfg_dir}/${regex_match}" 548 | done 549 | fi 550 | 551 | configfile "${prefix}/grub.cfg.d/default" 552 | --------------------------------------------------------------------------------