#! /bin/bash PKGLIST="" QUIET="y" FORCE="n" PACCONFIG="/etc/pacman.conf" APPNAME=$(basename "${0}") # usage: usage <exitvalue> usage () { echo "usage ${APPNAME} [options] command <command options>" echo " general options:" echo " -f Force overwrite of working files/squashfs image/bootable image" echo " -p PACKAGE(S) Additional package(s) to install, can be used multiple times" echo " -C <file> Config file for pacman. Default $PACCONFIG" echo " -v Enable verbose output." echo " -h This message." echo " commands:" echo " create <dir>" echo " create a base directory layout to work with" echo " includes all specified packages" echo " iso -p <bootloader> <dir> <image name>" echo " build an iso image from the working dir" echo " usb -p <bootloader> <dir> <image name>" echo " build an iso image from the working dir" exit $1 } while getopts 'i:P:p:a:t:fvh' arg; do case "${arg}" in p) PKGLIST="${PKGLIST} ${OPTARG}" ;; C) PACCONFIG="${OPTARG}" ;; f) FORCE="y" ;; v) QUIET="n" ;; h|?) usage 0 ;; *) echo "invalid argument '${arg}'"; usage 1 ;; esac done #trim spaces PKGLIST="$(echo $PKGLIST)" shift $(($OPTIND - 1)) # do UID checking here so someone can at least get usage instructions if [ "$EUID" != "0" ]; then echo "error: This script must be run as root." exit 1 fi if [ ! -f "$PACCONFIG" ]; then echo "error: pacman config file '$PACCONFIG' does not exist" exit 1 fi command_name="${1}" work_dir="" imgname="" case "${command_name}" in create) work_dir="${2}"; imgname="none" ;; iso) work_dir="${2}"; imgname="${3}" ;; usb) work_dir="${2}"; imgname="${3}" ;; *) echo "invalid command name '${command_name}'"; usage 1 ;; esac [ "x${imgname}" = "x" ] && echo "Image name must be specified" && usage 1 [ "x${work_dir}" = "x" ] && echo "Please specify a working directory" && usage 1 echo "${APPNAME} : Configuration Settings" echo " working directory: ${work_dir}" echo " image name: ${imgname}" # usage: _pacman <packages>... _pacman () { local ret if [ "${QUIET}" = "y" ]; then mkarchroot -C "$PACCONFIG" -f "${work_dir}/root-image" $* 2>&1 >/dev/null ret=$? else mkarchroot -C "$PACCONFIG" -f "${work_dir}/root-image" $* ret=$? fi # Cleanup find "${work_dir}" -name *.pacnew -name *.pacsave -name *.pacorig -delete if [ $ret -ne 0 ]; then exit 1 fi } command_create () { echo "====> Creating working directory: ${work_dir}" mkdir -p "${work_dir}/iso/" mkdir -p "${work_dir}/root-image/" echo "# archiso isomounts file # img - location of image/directory to mount relative to addons directory # arch - architecture of this image # mount point - absolute location on the post-initrd root # type - either 'bind' or 'squashfs' for now # syntax: <img> <arch> <mount point> <type> # NOTE: Order matters. If the same file exists in multiple # images, the FIRST one mounted, top-down, will take precedence root-image.sqfs i686 / squashfs #root-image-x86_64.sqfs x86_64 / squashfs" > "${work_dir}/isomounts" echo "README for this archiso created directory All directories in this dir, except for 'iso' will be squashed with squashfs and put into the iso dir as iso/<dirname>.sqfs This should be reflected in the isomounts file The iso dir is later used to build the actual bootable iso. Please ensure the proper bootloader is installed or copied to the iso/ directory. ...TODO: Write more..." > "${work_dir}/README" if [ "${PKGLIST}" != "" ]; then echo "====> Installing packages to '${work_dir}/root-image/'" _pacman "${PKGLIST}" echo "Cleaning up what we can" if [ -d "${work_dir}/root-image/boot/" ]; then # remove the initcpio images that were generated for the host system find "${work_dir}/root-image/boot" -name *.img -delete fi #TODO is this needed? do it at the Makefile level? if [ -d "${work_dir}/root-image/home/" ]; then echo "Creating default home directory" install -d -o1000 -g100 -m0755 "${work_dir}/root-image/home/arch" fi # delete a lot of unnecessary cache/log files kill_dirs="var/abs var/cache/man var/cache/pacman var/lib/pacman/sync var/log/* var/mail tmp/* initrd" for x in ${kill_dirs}; do if [ -e "${work_dir}/root-image/${x}" ]; then rm -rf "${work_dir}/root-image/${x}" fi done fi } # _mksquash dirname _mksquash () { echo "_mksquash $1" if [ ! -d "$1" ]; then echo "Error: '$1' is not a directory" return 1 fi sqimg="${work_dir}/iso/$(basename ${1}).sqfs" echo "====> Generating SquashFS image for '${1}'" if [ -e "${sqimg}" ]; then if [ "${FORCE}" = "y" ]; then echo -n "Removing old SquashFS image..." rm "${sqimg}" echo "done." else echo "error: SquashFS image '${sqimg}' already exists, aborting." exit 1 fi fi echo "Creating SquashFS image. This may take some time..." start=$(date +%s) if [ "${QUIET}" = "y" ]; then mksquashfs "${1}" "${sqimg}" -noappend >/dev/null else mksquashfs "${1}" "${sqimg}" -noappend fi minutes=$(echo $start $(date +%s) | awk '{ printf "%0.2f",($2-$1)/60 }') echo "Image creation done in $minutes minutes." } _imgcommon () { for d in $(find "${work_dir}" -maxdepth 1 -type d -name '[^.]*'); do if [ "$d" != "${work_dir}/iso" -a \ "$(basename "$d")" != "iso" -a \ "$d" != "${work_dir}" ]; then _mksquash "$d" fi done echo "====> Making bootable image" # Sanity checks if [ ! -d "${work_dir}/iso" ]; then echo "Error: '${work_dir}/iso' doesn't exist. What did you do?!" exit 1 fi if [ ! -f "${work_dir}/isomounts" ]; then echo "Error: the isomounts file doesn't exist. This image won't do anything" echo " Protecting you from yourself and erroring out here..." exit 1 fi if [ -e "${imgname}" ]; then if [ "${FORCE}" = "y" ]; then echo "Removing existing bootable image..." rm -rf "${imgname}" else echo "error: Image '${imgname}' already exists, aborting." exit 1 fi fi cp "${work_dir}/isomounts" "${work_dir}/iso/" } command_iso () { _imgcommon bootflags="" if [ "$PKGLIST" = "grub" -o "$PKGLIST" = "grub-gfx" ]; then if [ ! -e "${work_dir}/iso/boot/grub/stage2_eltorito" ]; then echo "error: grub stage files not found in '${work_dir}/iso/boot/grub'" exit 1 fi bootflags="-b boot/grub/stage2_eltorito" elif [ "$PKGLIST" = "syslinux" ]; then if [ ! -e "${work_dir}/iso/boot/isolinux/isolinux.bin" ]; then echo "error: isolinux bin file not found in '${work_dir}/iso/boot/isolinux'" exit 1 fi bootflags="-b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat" else echo "No bootloader specified. Use the -p flag to specify" echo " Supported Bootloaders:" echo " grub" echo " grub-gfx" echo " syslinux" exit 1 fi echo "Creating ISO image..." qflag="" [ "${QUIET}" = "y" ] && qflag="-q" mkisofs ${qflag} -r -l $bootflags -uid 0 -gid 0 \ -input-charset utf-8 -p "prepared by mkarchiso" \ -no-emul-boot -boot-load-size 4 -boot-info-table \ -publisher "ArchLinux <archlinux.org>" \ -A "ArchLinux Live/Rescue CD" \ -o "${imgname}" "${work_dir}/iso/" } command_usb () { _imgcommon fsimg="${imgname}.part1" # ext2 overhead's upper bound is 6%, empirically tested up to 1GB rootsize=$(du -bs "${work_dir}/iso" | cut -f1) imgsz=$(( (${rootsize}*106)/100/512 + 1)) # image size in sectors # create the filesystem image file dd if=/dev/zero of="$fsimg" bs=512 count="$imgsz" # create a filesystem on the image mke2fs -m 0 -F "$fsimg" # mount the filesystem and copy data modprobe loop TMPDIR=$(mktemp -d archiso-usbXXXXXX) mount -o loop "$fsimg" "$TMPDIR" cp -a "${work_dir}"/iso/* "$TMPDIR" umount "$TMPDIR" rm -rf "$TMPDIR" # add sectors 0-62, then glue together dd if=/dev/zero of="${imgname}" bs=512 count=63 cat "$fsimg" >> "${imgname}" rm "$fsimg" # create a partition table # if this looks like voodoo, it's because it is sfdisk -uS -f "${imgname}" << EOF 63,$imgsz,83,* 0,0,00 0,0,00 0,0,00 EOF # install grub on the image grub --no-floppy --batch << EOF device (hd0) ${imgname} root (hd0,0) setup (hd0) EOF } # Go through the main commands in order. If 'all' was specified, then we want # to do everything. Start with 'install'. if [ "${command_name}" = "create" ]; then command_create fi if [ "${command_name}" = "iso" ]; then command_iso fi if [ "${command_name}" = "usb" ]; then command_usb fi # vim:ts=4:sw=4:et: