#!/bin/sh # Boilerplate _root="$(dirname "${0}")" . "${_root}/lib/env.sh" # Load needed modules want root ansi log ask targets="prepwork admin packages patch overlay prepboot preptmp prepetc prepvar imgboot imgconf imgetc imgvar custom" pebkac() { [ "${*}" ] && printf "${*}\n\n" echo "Usage: $(basename "${0}") <-b basedir> <-t stagedir> <-i pkgsdir> <-p patchdir>" echo " <-r rootdir> <-l logfile> [-h] " echo ' -b basedir Basedir for automagic defaults' echo ' -t stagedir Staging directory name (Default: ${base}/stage)' echo ' -i pkgsdir Directory holding packages to install (Default: ${base}/pkg)' echo ' -p patchdir Directory holding patches to apply (Default: ${base}/patch)' echo ' -o overlaydir Directory holding an overlay tree (Default: ${base}/overlay)' echo ' -r rootdir Directory holding the virgin source tree (Default: ${root})' echo ' -l logfile File to hold stderr spam (Default: ${stage}/gentree.log' echo ' -h Hello! >^-^<' echo '' echo 'Available targets:' for target in ${targets} do echo " ${target}" done exit 1 } while getopts "b:t:i:p:o:r:l:h" opt do case "${opt}" in b) base="${OPTARG}" ;; t) stage="${OPTARG}" ;; i) pkgs="${OPTARG}" ;; p) patch="${OPTARG}" ;; o) overlay="${OPTARG}" ;; r) root="${OPTARG}" ;; l) logfile="${OPTARG}" ;; h) pebkac ;; [?]) pebkac "Unrecognized option ${opt}" ;; esac done shift $(( $OPTIND - 1 )) sequence="${*:-${targets}}" base="${base:-/usr/home/cyberleo/world}" base="$(realpath "${base}")" stage="${stage:-${base}/tree}" pkgs="${pkgs:-${base}/pkg}" patch="${patch:-${base}/patch}" overlay="${overlay:-${base}/overlay}" root="${root:-${root}}" logfile="${logfile:-${stage}/gentree.log}" mkdir -p "${stage}" stage="$(realpath "${stage}")" sroot="${stage}/root" sboot="${stage}/boot" sconf="${stage}/conf" exec 2>>"${logfile}" _log_to_stderr=yes # Helper functions onelink() { # Make sure the provided file(s) have only one link! while [ -n "${1}" ] do if [ -f "${1}" -a "$(stat -f '%l' "${1}")" -gt 1 ] then cp -p "${1}" "${1}.tmp" && mv "${1}.tmp" "${1}" || err "breaklink failed" fi shift done } # Build steps do_prepwork() { log Prepare workspace if [ -d "${stage}" -a \( -d "${sroot}" -o -d "${sboot}" -o -d "${sconf}" \) ] then yn n " ${a_yellow}*${a_normal} Workspace already exists. Delete? [y/N]" || err Aborting chk rm -Rf "${sroot}" "${sboot}" "${sconf}" fi chk mkdir -p "${sroot}" # Eliminate schg, because it interferes with hardlinks chk chflags -R noschg "${root}/lib" chk chflags -R noschg "${root}/libexec" chk chflags -R noschg "${root}/sbin" chk chflags -R noschg "${root}/usr" ( cd "${root}" && find . | cpio -p --link "${sroot}" ) || chk } do_admin() { log Create an emergency user admin/admin # delink passwd to ensure it doesn't get patched in-place chk onelink "${sroot}/etc/passwd" "${sroot}/etc/master.passwd" echo '$1$2rXOWsK/$eiBHA6K7xL96DZbcY24YR0' | chk chroot "${sroot}" /usr/sbin/pw useradd admin -u 999 -g wheel -G operator -c Administrator -d /usr/home/admin -m -s /bin/csh -H 0 } do_packages() { if [ -d "${pkgs}" -a "$(ls -1 "${pkgs}" | wc -l)" -gt 0 ] then count="$(ls -1 "${pkgs}" | wc -l)" log Install ${count} packages from "${pkgs}" chk mkdir -p "${sroot}/pkg" ( cd "${pkgs}" && find . | cpio -p --link "${sroot}/pkg" ) || chk chk chroot "${sroot}" /bin/sh -c 'cd /pkg; exec pkg_add -F *' chk rm -Rf "${sroot}/pkg" fi } do_patch() { log Apply patches from "${patch}" for file in "${patch}"/* do note "... $(basename "${file}")" ( cd "${sroot}" && patch < "${file}" ) || chk # Remove .orig files sed -e '/^+++ /!d; s/^+++ //; s/[ ]*[0-9: .+-]*$//' "${file}" | while read target do rm -f "${sroot}/${target}.orig" done || chk done } do_overlay() { log Apply overlay from "${overlay}" [ -d "${overlay}" ] || return ( cd "${overlay}" && find . | cpio -p "${sroot}" ) || chk } do_prepboot() { log Prepare /boot chk mv "${sroot}/boot/boot" "${sroot}/boot/boot.blk" chk ln -sf . "${sroot}/boot/boot" } do_preptmp() { log Prepare /tmp ( cd "${sroot}/tmp" && find . | cpio -p --link ../var/tmp ) || chk chk rm -Rf "${sroot}/tmp" chk ln -sf var/tmp "${sroot}/tmp" } do_prepetc() { log Prepare /etc chk mkdir -p "${sroot}/etc/local" chk mkdir -p "${sroot}/usr/local/etc" # Silence warnings ( cd "${sroot}/usr/local/etc" && find . | cpio -p --link ../../../etc/local ) || chk chk rm -Rf "${sroot}/usr/local/etc" chk ln -sf ../../etc/local "${sroot}/usr/local/etc" } do_prepvar() { log Prepare /var # Nothing necessary here } do_imgboot() { log Create boot imgsrc chk mv "${sroot}/boot" "${stage}" chk mkdir -p "${sroot}/boot" chk rm -f "${sboot}/kernel"/*.symbols # Gzipped kernel is okay chk onelink "${sboot}/kernel/kernel" chk gzip -9 "${sboot}/kernel/kernel" # Compress all files in /boot/kernel # Loader cannot handle gzipped modules. Decompress the required modules # kldload cannot handle gzipped modules either #find "${sboot}/kernel" -type f | xargs gzip -9f || chk #cat "${sboot}/loader.conf" | grep '_load=' | sed -e 's/^\(.*\)_load=.*$/\1/' | while read mod #do # [ -f "${sboot}/kernel/${mod}.ko.gz" ] && gunzip "${sboot}/kernel/${mod}.ko.gz" || chk #done # # Instead: put all modules in the root image, except those needed to boot the kernel chk mkdir -p "${sroot}/boot/boot" chk mkdir -p "${sroot}/boot/kernel" # Link all modules into the root fs ( cd "${sboot}/kernel" && find . -name '*.ko' -o -name 'linker.hints' | cpio -p --link "${sroot}/boot/kernel" ) || chk # Remove all modules from the root fs that are preloaded by the loader cat "${sboot}/loader.conf" | grep '_load=' | sed -e 's#^\(.*\)_load=.*$#'"${sroot}/boot/kernel/"'\1.ko#' | xargs rm -f # Remove all modules from the boot fs that are present in the root fs ( cd "${sroot}/boot/kernel" && ls -1 ) | sed -e 's#^#'"${sboot}/kernel/"'#' | xargs rm -f # Link the preloaded modules from the boot fs to the root fs, to provide a homogenous view ( cd "${sboot}/kernel" && ls -1 ) | while read mod do ln -sf "../boot/kernel/${mod}" "${sroot}/boot/kernel/${mod}" done } do_imgconf() { log Create conf imgsrc chk mkdir -p "${sroot}/conf" echo "ufs:/dev/ufs/conf" > "${sroot}/conf/diskless_remount" || chk chk mkdir -p "${sconf}/backup" chk mkdir -p "${sconf}/base/etc" chk mkdir -p "${sconf}/base/var" chk mkdir -p "${sconf}/default/etc" chk mkdir -p "${sconf}/default/var" } do_imgetc() { log Create etc imgsrc chk touch "${sroot}/etc/diskless" chk sleep 1 # Make sure diskless is at least one second older than everything in custom chk mv "${sroot}/etc" "${stage}" chk mkdir -p "${sroot}/etc" chk cp -p "${stage}/etc/rc" "${stage}/etc/rc.subr" "${stage}/etc/rc.initdiskless" "${stage}/etc/login.conf.db" "${stage}/etc/diskless" "${sroot}/etc" echo "10240" > "${sconf}/base/etc/md_size" || chk ( cd "${stage}" && find etc | cpio -o ) | gzip -9 > "${sconf}/base/etc.cpio.gz" || chk chk rm -Rf "${stage}/etc" } do_imgvar() { log Create var imgsrc chk mv "${sroot}/var" "${stage}" chk mkdir -p "${sroot}/var" echo "131072" > "${sconf}/base/var/md_size" || chk ( cd "${stage}" && find var | cpio -o ) | gzip -9 > "${sconf}/base/var.cpio.gz" || chk chk rm -Rf "${stage}/var" } do_custom() { log Patch in custom config ( cd "${base}/conf" && find . | cpio -p --link "${sconf}/default" ) || chk # Make sure files in default are newer than the tagfile, so they will be caught by saveconfig find "${sconf}/default" -type f -print0 | xargs -0 touch } for step in ${sequence} do echo "${targets}" | grep -q "${step}" || err Unrecognized target "${step}" do_${step} done