#!/bin/bash # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public # License along with this program. If not, see # . # Load. source $APP_BASE/lib/hydra/functions || exit 1 hydra_config_load # Set bootless folder function hydra_bootless_folder { # Check for a bootless repository if [ -e "$HYDRA_FOLDER/bootless" ]; then BOOTLESS_DIR="$HYDRA_FOLDER/bootless" elif [ -e "$HYDRA_FOLDER/conf/bootless" ]; then BOOTLESS_DIR="$HYDRA_FOLDER/conf/bootless" else echo "Please make a symlink $HYDRA_FOLDER/bootless pointing to your bootless repository." exit 1 fi } # Sync bootless folder function hydra_bootless_rsync { # Exclude git folder and follow git-annex symlinks $sudo rsync -CLavz --exclude boot $1/ $2/ # Make sure there's a symlink to itself ( cd $2 && ln -s . boot ) } # Make a boot device function hydra_bootless_make { # Set folder hydra_bootless_folder # Set sudo config local sudo device rsync if [ "`whoami`" != 'root' ]; then sudo="sudo" fi # Script description cat < /dev/null ) if [ $? -ne 0 ]; then echo "Error: '${BOOTLESS_DIR}' is not a git repository." exit 1 fi if [ -z "$1" ]; then echo -n "Target device: " read device else device=$1 fi if [ "$2" == "--rsync" ]; then rsync="true" fi usbdevice=`echo ${device} | sed -e s/[0-9]\$//g` # Issue a warning hydra_bootless_warning ${device} if [ "$?" != "0" ]; then echo "Aborting..." exit 1 fi hydra_bootless_device ${usbdevice} ${device} # Mount tmpdir=`mktemp --tmpdir=/tmp -d` $sudo mount ${device} ${tmpdir} if [ $? != 0 ]; then echo "Error: failed to mount \"${device}\" filesystem in \"${tmpdir}\" (errno: $?)." exit 1 fi # Copy data if [ "$rsync" == "true" ]; then hydra_bootless_rsync ${BOOTLESS_DIR} ${tmpdir}/boot else #$sudo git clone --depth=1 file:///${BOOTLESS_DIR} ${tmpdir}/boot $sudo git clone file:///${BOOTLESS_DIR} ${tmpdir}/boot if [ $? != 0 ]; then echo "Error: failed to clone repository \"${BOOTLESS_DIR}\" in \"${tmpdir}\" (errno: $?)." exit 1 fi ( cd ${tmpdir}/boot && $sudo git annex init && \ $sudo git config annex.sshcaching false && \ $sudo git annex untrust here && $sudo git annex sync && $sudo git annex get . ) if [ $? != 0 ]; then echo "Error: failed to get files into repository \"${BOOTLESS_DIR}\" in \"${tmpdir}\" (errno: $?)." exit 1 fi fi # Grub legacy #devicemap=`mktemp` #grub-mkdevicemap --no-floppy --device-map=${devicemap} #usbdevice=`echo ${device} | sed -e s/[0-9]\$//` #grubroot=`grep "${usbdevice}" ${devicemap} | cut -f1` #grubdevice=`echo ${grubroot} | sed -e s/\)/,0\)/` #echo "root ${grubdevice} #setup ${grubroot} #quit" | grub --device-map=${devicemap} --batch # Grub 2 $sudo grub-install --boot-directory=${tmpdir} --skip-fs-probe --force ${usbdevice} if [ $? != 0 ]; then echo "Error: grub-install failed (errno: $?)." exit 1 fi # TODO: consider an alternative scenario: # https://www.gnu.org/software/grub/manual/html_node/Installing-GRUB-using-grub_002dinstall.html#Installing-GRUB-using-grub_002dinstall #sudo losetup /dev/loop0 /dev/sdb1 #sudo mount /dev/loop0 /media/usb/ #sudo grub-install --boot-directory=/media/usb/ --allow-floppy --force /dev/loop0 # Finalize #rm -f ${devicemap} $sudo umount ${tmpdir} $sudo rm -rf ${tmpdir} } # Update a boot device function hydra_bootless_update { # Set folder hydra_bootless_folder # Set sudo config local sudo if [ "`whoami`" != 'root' ]; then sudo="sudo" fi if [ -z "$1" ]; then echo -n "Target device: " read device else device=$1 fi # Target device consistency check if [ ! -b ${device} ]; then echo "Error: device \"${device}\" not found." exit 1 elif [ ! -d "${tmpdir}/boot" ]; then echo "Error: bootless repository not found at ${device}." exit 1 fi tmpdir=`mktemp --tmpdir=/tmp -d` $sudo mount ${device} ${tmpdir} if [ $? != 0 ]; then echo "Error: failed to mount \"${device}\" filesystem in \"${tmpdir}\" (errno: $?)." exit 1 fi # Copy data if [ -d "${tmpdir}/boot/.git" ]; then #( cd ${tmpdir}/boot && $sudo git pull origin master ) ( cd ${tmpdir}/boot && $sudo git annex sync && $sudo git annex get . ) else hydra_bootless_rsync ${BOOTLESS_DIR} ${tmpdir}/boot fi # Finalize $sudo umount ${tmpdir} $sudo rm -rf ${tmpdir} } # Repository initiator function hydra_bootless_init { mkdir -p $HYDRA_FOLDER if [ ! -z "$1" ]; then # Clone from url git clone $1 $HYDRA_FOLDER/bootless && ( cd $HYDRA_FOLDER/bootless && git annex init && git config annex.sshcaching false && git annex sync ) exit $? fi # Create a fresh repository mkdir -p $HYDRA_FOLDER/bootless/{default,custom,grub} mkdir -p $HYDRA_FOLDER/bootless/default/{debian,memtest,ubuntu} touch $HYDRA_FOLDER/bootless/{default,custom,grub}/.empty touch $HYDRA_FOLDER/bootless/default/{debian,memtest,ubuntu}/.empty ( cd $HYDRA_FOLDER/bootless && ln -s . boot) if [ -f "/boot/memtest86+.bin" ]; then cp /boot/memtest86+.bin $HYDRA_FOLDER/bootless/default/memtest else echo "No memtest image found. Please install memtest86+ package" echo "and manually copy /boot/memtest86+.bin if you want memtest support" fi # Grub configuration cat > $HYDRA_FOLDER/bootless/grub/grub.cfg <<-EOF # This is grub.cfg for use with Bootless Management System ### BEGIN header ### if [ -s $prefix/grubenv ]; then load_env fi set default="0" if [ "${prev_saved_entry}" ]; then set saved_entry="${prev_saved_entry}" save_env saved_entry set prev_saved_entry= save_env prev_saved_entry set boot_once=true fi function savedefault { if [ -z "${boot_once}" ]; then saved_entry="${chosen}" save_env saved_entry fi } function load_video { } set timeout=5 ### END header ### ### BEGIN debian_theme ### set menu_color_normal=white/blue set menu_color_highlight=yellow/red ### END debian_theme ### EOF # Initialize git repository ( cd $HYDRA_FOLDER/bootless git init git annex init git config annex.sshcaching false git add boot git add {default,custom,grub}/.empty git add default/{debian,memtest,ubuntu}/.empty git add grub/grub.cfg git annex add . git commit -a -m "Initial import" ) echo "Now add your boot images and edit $HYDRA_FOLDER/bootless/grub/grub.cfg to suit your needs." } # Git wrapper function hydra_bootless_git { # Set folder hydra_bootless_folder ( cd $BOOTLESS_DIR && git $* ) } # Create a target device function hydra_bootless_device { local device="$1" local subdevice="$2" # Erase disk MBR # http://askubuntu.com/questions/158299/why-does-installing-grub2-give-an-iso9660-filesystem-destruction-warning #sudo dd if=/dev/zero of=/dev/${usbdevice} bs=512 seek=1 count=2047 # Check if device is mounted if [ "`mount | grep ${subdevice}`" != "" ]; then echo "Error: device \"${subdevice}\" is mounted." exit 1 fi # Target device consistency check if [ ! -b ${subdevice} ]; then echo "Error: device \"${subdevice}\" not found." exit 1 fi # Remove old partitions for partition in `$sudo parted -s -- ${device} print | awk '/^ / {print $1}'`; do $sudo parted -s -- ${device} rm $partition done # Create a single partition $sudo parted -s -- ${device} mkpart primary ext2 2 -1 $sudo parted -s -- ${device} set 1 boot on # Create filesystem $sudo mke2fs ${subdevice} if [ $? != 0 ]; then echo "Error: mke2fs failed in \"${subdevice}\" (errno: $?)." exit 1 fi # Tune $sudo tune2fs -c 0 -i 0 ${subdevice} if [ $? != 0 ]; then echo "Error: tune2fs failed in \"${subdevice}\" (errno: $?)." exit 1 fi } function hydra_bootless_warning { local device=$1 local go echo "******************" echo "* ATTENTION!!! *" echo "******************" echo "" echo "If you continue, all data in device \"${device}\" will be destroyed!" echo "" echo -n "Are you sure you want to continue? Type uppercase \"YES\": " read go if [ "${go}" != "YES" ]; then false else true fi } # Generate a bootless disk image function hydra_bootless_image { local output="$1" local device="$2" # Fix parameters if echo ${output} | grep -q "/dev/"; then output="bootless.img" device="$1" elif [ -z "$output" ]; then output="bootless.img" fi # Set folder hydra_bootless_folder # Set sudo config local sudo if [ "`whoami`" != 'root' ]; then sudo="sudo" fi # Copy data tmpdir=`mktemp --tmpdir=/tmp -d` hydra_bootless_rsync ${BOOTLESS_DIR} ${tmpdir}/boot # Make rescue disk grub-mkrescue -o ${output} ${tmpdir} # Optionally copy to removable media if [ ! -z "$device" ]; then # Issue a warning hydra_bootless_warning ${device} if [ "$?" != "0" ]; then echo "Skipping copy of ${output} to ${device}..." else # Check if device is mounted if [ "`mount | grep ${device}`" != "" ]; then echo "Error: device \"${device}\" is mounted." echo "Skipping copy of ${output} to ${device}..." else $sudo dd if=${output} of=${device} fi fi fi # Cleanup rm -rf ${tmpdir} # Finish if [ -z "$device" ]; then echo "Image saved at ${output}" else echo "Removing image ${output}..." rm ${output} fi echo "Done." } # Usage function hydra_bootless_usage { echo "Usage: `basename $0` [arguments]" exit 1 } # Check for requirements for req in parted git-annex; do hydra_install_package $req done # Parameter verification if [ -z "$1" ]; then hydra_bootless_usage elif [ -z "$HYDRA_FOLDER" ]; then echo "Parameter HYDRA_FOLDER not configured." exit 1 elif [ "$1" == "make" ]; then shift hydra_bootless_make $* elif [ "$1" == "init" ]; then shift hydra_bootless_init $* elif [ "$1" == "update" ]; then shift hydra_bootless_update $* elif [ "$1" == "git" ]; then shift hydra_bootless_git $* elif [ "$1" == "image" ]; then shift hydra_bootless_image $* else hydra_bootless_usage fi