147 lines
4.2 KiB
Bash
Executable file
147 lines
4.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "${root}"
|
|
|
|
netboot_root="${EVERY_CHANNEL_NETBOOT_ROOT:-tmp/netboot}"
|
|
tftp_dir="${netboot_root}/tftp"
|
|
netboot_hostname="${EVERY_CHANNEL_NETBOOT_HOSTNAME:-boot.every.channel}"
|
|
http_port="${EVERY_CHANNEL_NETBOOT_HTTP_PORT:-8080}"
|
|
ipxe_repo="${EVERY_CHANNEL_NETBOOT_IPXE_REPO:-https://github.com/ipxe/ipxe.git}"
|
|
ipxe_ref="${EVERY_CHANNEL_NETBOOT_IPXE_REF:-}"
|
|
output_name="${EVERY_CHANNEL_NETBOOT_IPXE_FILENAME:-ec-ipxe.efi}"
|
|
use_docker="${EVERY_CHANNEL_NETBOOT_IPXE_USE_DOCKER:-auto}"
|
|
docker_image="${EVERY_CHANNEL_NETBOOT_IPXE_DOCKER_IMAGE:-ubuntu:24.04}"
|
|
docker_platform="${EVERY_CHANNEL_NETBOOT_IPXE_DOCKER_PLATFORM:-linux/amd64}"
|
|
chain_token="${EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN:-}"
|
|
|
|
need_cmd() {
|
|
local name="$1"
|
|
if ! command -v "${name}" >/dev/null 2>&1; then
|
|
echo "error: required command not found: ${name}" >&2
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
validate_chain_token() {
|
|
local value="$1"
|
|
if [[ -z "${value}" ]]; then
|
|
return 0
|
|
fi
|
|
if [[ ! "${value}" =~ ^[A-Za-z0-9._~-]{16,128}$ ]]; then
|
|
echo "error: EVERY_CHANNEL_NETBOOT_CHAIN_TOKEN must match [A-Za-z0-9._~-]{16,128}" >&2
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
validate_chain_token "${chain_token}"
|
|
|
|
tmp_dir="$(mktemp -d)"
|
|
cleanup() {
|
|
rm -rf "${tmp_dir}"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
repo_dir="${tmp_dir}/ipxe"
|
|
embed_script="${tmp_dir}/embed.ipxe"
|
|
build_output="${tmp_dir}/ipxe.efi"
|
|
|
|
chain_url="http://${netboot_hostname}:${http_port}/netboot.ipxe"
|
|
if [[ -n "${chain_token}" ]]; then
|
|
chain_url="${chain_url}?token=${chain_token}"
|
|
fi
|
|
|
|
cat > "${embed_script}" <<EOF
|
|
#!ipxe
|
|
dhcp
|
|
chain ${chain_url} || shell
|
|
EOF
|
|
|
|
bool_norm() {
|
|
local raw
|
|
raw="$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')"
|
|
case "${raw}" in
|
|
''|auto) echo "auto" ;;
|
|
true|1|yes|y|on) echo "true" ;;
|
|
false|0|no|n|off) echo "false" ;;
|
|
*)
|
|
echo "error: invalid boolean value '${1}'" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
}
|
|
|
|
build_native() {
|
|
need_cmd git
|
|
need_cmd make
|
|
if ! command -v cc >/dev/null 2>&1 && ! command -v gcc >/dev/null 2>&1; then
|
|
echo "error: required C compiler not found (need cc or gcc)" >&2
|
|
return 1
|
|
fi
|
|
if ! command -v objcopy >/dev/null 2>&1; then
|
|
echo "error: required tool not found: objcopy" >&2
|
|
return 1
|
|
fi
|
|
git clone --depth 1 "${ipxe_repo}" "${repo_dir}" >/dev/null
|
|
if [[ -n "${ipxe_ref}" ]]; then
|
|
git -C "${repo_dir}" fetch --depth 1 origin "${ipxe_ref}" >/dev/null
|
|
git -C "${repo_dir}" checkout -q FETCH_HEAD
|
|
fi
|
|
make -C "${repo_dir}/src" -s "bin-x86_64-efi/ipxe.efi" "EMBED=${embed_script}"
|
|
cp -f "${repo_dir}/src/bin-x86_64-efi/ipxe.efi" "${build_output}"
|
|
}
|
|
|
|
build_docker() {
|
|
need_cmd docker
|
|
docker run --rm \
|
|
--platform "${docker_platform}" \
|
|
-v "${tmp_dir}:/work" \
|
|
-e IPXE_REPO="${ipxe_repo}" \
|
|
-e IPXE_REF="${ipxe_ref}" \
|
|
-w /work \
|
|
"${docker_image}" \
|
|
bash -lc '
|
|
set -euo pipefail
|
|
apt-get update >/dev/null
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
ca-certificates git make gcc binutils perl mtools >/dev/null
|
|
git clone --depth 1 "${IPXE_REPO}" ipxe >/dev/null
|
|
if [ -n "${IPXE_REF}" ]; then
|
|
git -C ipxe fetch --depth 1 origin "${IPXE_REF}" >/dev/null
|
|
git -C ipxe checkout -q FETCH_HEAD
|
|
fi
|
|
make -C ipxe/src -s "bin-x86_64-efi/ipxe.efi" "EMBED=/work/embed.ipxe"
|
|
'
|
|
cp -f "${tmp_dir}/ipxe/src/bin-x86_64-efi/ipxe.efi" "${build_output}"
|
|
}
|
|
|
|
use_docker="$(bool_norm "${use_docker}")"
|
|
build_ok="false"
|
|
if [[ "${use_docker}" != "true" ]]; then
|
|
if build_native; then
|
|
build_ok="true"
|
|
fi
|
|
fi
|
|
if [[ "${build_ok}" != "true" ]]; then
|
|
if [[ "${use_docker}" == "false" ]]; then
|
|
exit 2
|
|
fi
|
|
build_docker
|
|
fi
|
|
|
|
mkdir -p "${tftp_dir}"
|
|
cp -f "${build_output}" "${tftp_dir}/${output_name}"
|
|
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
sha256sum "${tftp_dir}/${output_name}" > "${tftp_dir}/${output_name}.sha256"
|
|
elif command -v shasum >/dev/null 2>&1; then
|
|
shasum -a 256 "${tftp_dir}/${output_name}" > "${tftp_dir}/${output_name}.sha256"
|
|
fi
|
|
|
|
echo "ok: built embedded iPXE binary: ${tftp_dir}/${output_name}"
|
|
echo "ok: chains to ${chain_url}"
|
|
if [[ -n "${chain_token}" ]]; then
|
|
echo "ok: chain token enabled"
|
|
fi
|
|
echo "hint: in UniFi set DHCP boot filename to ${output_name}"
|