#!/bin/bash # Copyright (C) 2016-2018 Iru Cai # SPDX-License-Identifier: GPL-3.0-or-later set -e DISTROLIST=() ISOLIST=() BOOTPATH= ROOTPATH= DEVNAME= KERNELDIR= DATADIR= INSTALL_GRUB=1 GRUBCFG_ONLY=0 DOWNLOAD_ONLY=0 UMOUNT_BOOT=0 UMOUNT_ROOT=0 CLEAN_USB=0 . functions.sh msg() { echo -e "$1" >&2 } fatalerror() { msg "\x1b[1;31m$1\x1b[0m" exit 1 } usage() { >&2 cat << EOF $0 [--root ] [options] [--distro= ] [distro] ... use $0 -L to list available distros options: --root --root=: set the path to put the live USB data files --boot --boot=: set the path to put the kernel and loader files --clean: clean the old live USB files before installing --no-grub: do not install GRUB loader (still generate grub.cfg) --grubcfg: do not do real install, only generate grub.cfg file --downloadonly: only download the ISO files --dev=: write boot sector to (default is autodetected from ) EOF } try_mount() { local mnt if [[ "$1" =~ ^/dev ]] then if ! findmnt "$1" > /dev/null then udevil mount "$1" > /dev/null mnt=$(findmnt -n -o TARGET "$1") else mnt=$(findmnt -n -o TARGET "$1") fi else mnt="$1" fi echo "$mnt" } unset ISOPATH CFGFILE="$HOME/.liveusb-builder" test -f "$CFGFILE" && source "$CFGFILE" || true ISOPATH=${ISOPATH:-"$HOME/isofiles"} while [[ -n "$1" ]] do case "$1" in -L) exec ./listisos.sh ;; --boot=*) BOOTPATH="${1/--boot=}" ;; --boot) shift BOOTPATH="$1" ;; --root=*) ROOTPATH="${1/--root=}" ;; --root) shift ROOTPATH="$1" ;; --dev=*) DEVNAME=${1/--dev=} ;; --no-grub) INSTALL_GRUB=0 ;; --grubcfg) GRUBCFG_ONLY=1 ;; --downloadonly) DOWNLOAD_ONLY=1 ;; --clean) CLEAN_USB=1 ;; --distro=*) metadist="${1/--distro=}" if [ ! -f "distro/$metadist/meta" ] then fatalerror "distro/$metadist/meta not found" fi shift isofn="$1" if [ ! -f "$isofn" ] then fatalerror "File $isofn not found" fi DISTROLIST=(${DISTROLIST[@]} "/meta@$metadist@$isofn") ;; *=*|-*) usage exit 1 ;; *) if [ -f "distro/$1/isoinfo" ] then DISTROLIST=(${DISTROLIST[@]} "$1") else fatalerror "directory distro/$1/isoinfo not found" fi ;; esac shift done if [[ -z "$ROOTPATH" && "$DOWNLOAD_ONLY" == 0 ]]; then usage exit 1 fi _mnt="$(try_mount "$ROOTPATH")" if [ "${_mnt}" != "$ROOTPATH" ]; then ROOTPATH="${_mnt}" UMOUNT_ROOT=1 fi if [[ -z "$BOOTPATH" ]]; then BOOTPATH="$ROOTPATH" else _mnt="$(try_mount "$BOOTPATH")" if [ "${_mnt}" != "$BOOTPATH" ]; then BOOTPATH="${_mnt}" UMOUNT_BOOT=1 fi fi if [[ "$DOWNLOAD_ONLY" == 0 ]]; then UUID="$(findmnt -o UUID --raw --noheadings $ROOTPATH \ || fatalerror "UUID of $ROOTPATH not found, not a mountpoint?")" BOOTPART="$(findmnt -o SOURCE --raw --noheadings $BOOTPATH \ || fatalerror "$BOOTPATH seems not a mountpoint!")" # liveusb kernel and data should be placed on one disk # so get DEVNAME by the mountpoint of ROOTPATH is ok DEVNAME="/dev/$(getdiskbypart "/dev/disk/by-uuid/$UUID")" KERNELDIR="$BOOTPATH/liveusb-kernel" DATADIR="$ROOTPATH/liveusb-data" GRUBCFG="$BOOTPATH/grub/grub.cfg" SYSLINUXCFG="$BOOTPATH/syslinux/syslinux.cfg" install -d "$BOOTPATH/grub" "$BOOTPATH/syslinux" install -d "$KERNELDIR" "$DATADIR" msg "Files will be copy to $ROOTPATH" fi if [[ "${#DISTROLIST[@]}" == 0 ]]; then usage exit 1 fi if [ ! -z "$DEVNAME" ]; then msg "Boot sector will be written to $DEVNAME" fi if [[ "$CLEAN_USB" = 1 ]]; then msg "Cleaning old live USB files." rm -rf "$KERNELDIR"/* "$DATADIR"/* fi for i in ${DISTROLIST[@]} do if [[ "$i" == /meta@* ]] then # FIXME: we assume the meta file path and the iso file name # does not have '@' character ISOFILE="$(echo "$i" | cut -d@ -f3)" ISOLIST=("${ISOLIST[@]}" "$ISOFILE") # TODO: we just use the file name as the ISO name # can we do it better? ISONAMELIST=("${ISONAMELIST[@]}" "$(basename "$ISOFILE")") continue fi process_isoinfo "$i" ISOLIST=("${ISOLIST[@]}" "$ISOFILE") ISONAMELIST=("${ISONAMELIST[@]}" "$ISONAME") if [ -f "$ISOPATH/$ISOFILE" ] && checksum_verify; then true else download_iso fi done if [[ "$DOWNLOAD_ONLY" == 1 ]]; then exit 0 fi grubcfg_header > "$GRUBCFG" syslinux_header > "$SYSLINUXCFG" for i in `seq 1 ${#DISTROLIST[@]}` do ISOFILE="${ISOLIST[$i-1]}" ISONAME="${ISONAMELIST[$i-1]}" DISTRO="${DISTROLIST[$i-1]}" ISO_FILEPATH="$ISOPATH/$ISOFILE" if [[ "$DISTRO" == /meta@* ]] then metadir="$(echo "$DISTRO" | cut -d@ -f2)" _meta=meta_ ISO_FILEPATH="$ISOFILE" DISTRO="$metadir" set_distro "$metadir" KEYWORD="$ISONAME" source "distro/$metadir/meta" else _meta= ISO_FILEPATH="$ISOPATH/$ISOFILE" set_distro "$DISTRO" export DISTRONAME KEYWORD # for grub and syslinux generation process_distro "$DISTRO" fi if [ "$GRUBCFG_ONLY" == 0 ]; then install_live fi ${_meta}gen_grubcfg "$DISTRO" >> "$GRUBCFG" ${_meta}gen_syslinux "$DISTRO" >> "$SYSLINUXCFG" done if [ "$GRUBCFG_ONLY" == 1 ]; then msg 'Only generate grub.cfg and syslinux.cfg, will not install bootloader.' elif [ "$INSTALL_GRUB" == 1 ]; then read -p 'Answer Y to install bootloader.[Y/n] ' if [[ "$REPLY" =~ ^[Yy]|^$ ]]; then if ! as-root ./install_syslinux "$DEVNAME" "$BOOTPART" "$BOOTPATH"; then msg 'Failed to install syslinux, try to install GRUB as legacy BIOS bootloader.' as-root grub-install --force --boot-directory="$BOOTPATH" --target=i386-pc "$DEVNAME" fi as-root grub-install --boot-directory="$BOOTPATH" --efi-directory="$BOOTPATH" --bootloader-id=grub --target=x86_64-efi --removable --no-nvram "$DEVNAME" fi fi if [ "$UMOUNT_BOOT" == 1 ]; then msg 'Trying to umount the boot mountpoint, you may need to wait for sync() to complete.' udevil umount "$BOOTPATH" fi if [ "$UMOUNT_ROOT" == 1 -a "$ROOTPATH" != "$BOOTPATH" ]; then msg 'Trying to umount the root mountpoint, you may need to wait for sync() to complete.' udevil umount "$ROOTPATH" fi