From fa259627bf7c99a79cb48f27d57203fdc9e3df11 Mon Sep 17 00:00:00 2001 From: Mhd Sulhan Date: Fri, 20 Nov 2015 01:20:08 +0700 Subject: Modularize build script. All process to create rootfs and bootstrap are grouped into several functions. * scripts/rootfs.sh is for creating rootfs and installing packages. * scripts/bootstrap.sh is for bootstraping rootfs and cleaning. --- scripts/bootstrap.sh | 91 ++++++++++++ scripts/pacstrap.sh | 380 +++++++++++++++++++++++++++++++++++++++++++++++++++ scripts/rootfs.sh | 116 ++++++++++++++++ 3 files changed, 587 insertions(+) create mode 100755 scripts/bootstrap.sh create mode 100755 scripts/pacstrap.sh create mode 100755 scripts/rootfs.sh (limited to 'scripts') diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh new file mode 100755 index 0000000..ceb68f5 --- /dev/null +++ b/scripts/bootstrap.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +export LANG=C.UTF-8 +export HOSTNAME="arch-base" +export BOOT_LANG=en_GB.UTF-8 +export PKG_REMOVED=() + +strip_bin() { + find /usr/bin -type f \( -perm -0100 \) -print | + xargs file | + sed -n '/executable .*not stripped/s/: TAB .*//p' | + xargs -rt strip --strip-unneeded +} + +strip_lib() { + find /usr/lib -type f \( -perm -0100 \) -print | + xargs file | + sed -n '/executable .*not stripped/s/: TAB .*//p' | + xargs -rt strip --strip-unneeded +} + +bootstrap_clean_common() { + echo "==> cleaning ..." + strip_bin + strip_lib + rm -rf /usr/share/doc/* + rm -rf /usr/share/licenses/* + rm -rf /usr/share/locale/* + rm -rf /usr/share/man/* + rm -rf /usr/share/info/* + rm -rf /var/cache/pacman/pkg/* + rm -rf /var/log/* + rm -f /bootstrap.sh + rm -f /run_bootstrap.sh +} + +bootstrap_hostname() { + echo "==> set hostname ..." + echo ${HOSTNAME} > /etc/hostname +} + +bootstrap_timezone() { + echo "==> set timezone to UTC ..." + cp /usr/share/zoneinfo/UTC /etc/localtime +} + +bootstrap_locales() { + echo "==> set locales ..." + echo "en_GB.UTF-8 UTF-8" > /etc/locale.gen + echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen + + echo "==> generate locale ..." + /usr/bin/locale-gen + + echo "==> set locale preferences ..." + echo "LANG=${BOOT_LANG}" > "$rootfs"/etc/locale.conf + echo "LC_MESSAGES=C" >> "$rootfs"/etc/locale.conf +} + +bootstrap_remove_packages() { + echo "==> remove unneeded packages ..." + for pkg in ${PKGS_REMOVED[@]}; do + echo " removing $pkg" + pacman -Rdd --noconfirm $pkg + done +} + +bootstrap_clean_base() { + echo "==> cleaning base ..." + ## Remove all charmaps except UTF-8. + find /usr/share/i18n/charmaps/ \! -name "UTF-8.gz" -delete + ## Remove all locales except en_GB and en_US. + find /usr/share/i18n/locales/ \! -name "en_GB" \! -name "en_US" -delete + ## Remove all terminfo excetp ansi,cygwin,linux,screen-256color,vt100,vt220, + ## and xterm. + find /usr/share/terminfo/ \ + \! -name ansi \ + \! -name cygwin \ + \! -name linux \ + \! -name screen-256color \ + \! -name vt100 \ + \! -name vt220 \ + \! -name xterm \ + -delete + ## Remove all unneeded doc. + rm -rf /usr/share/texinfo/* + rm -rf /usr/share/zoneinfo/* + rm -rf /usr/share/iana-etc/* + rm -rf /usr/share/gtk-doc/* + rm -rf /usr/share/readline/* +} diff --git a/scripts/pacstrap.sh b/scripts/pacstrap.sh new file mode 100755 index 0000000..280eafa --- /dev/null +++ b/scripts/pacstrap.sh @@ -0,0 +1,380 @@ +#!/bin/bash + +# +# Assumptions: +# 1) User has partitioned, formatted, and mounted partitions on /mnt +# 2) Network is functional +# 3) Arguments passed to the script are valid pacman targets +# 4) A valid mirror appears in /etc/pacman.d/mirrorlist +# + +shopt -s extglob + +# generated from util-linux source: libmount/src/utils.c +declare -A pseudofs_types=([anon_inodefs]=1 + [autofs]=1 + [bdev]=1 + [binfmt_misc]=1 + [cgroup]=1 + [configfs]=1 + [cpuset]=1 + [debugfs]=1 + [devfs]=1 + [devpts]=1 + [devtmpfs]=1 + [dlmfs]=1 + [fuse.gvfs-fuse-daemon]=1 + [fusectl]=1 + [hugetlbfs]=1 + [mqueue]=1 + [nfsd]=1 + [none]=1 + [pipefs]=1 + [proc]=1 + [pstore]=1 + [ramfs]=1 + [rootfs]=1 + [rpc_pipefs]=1 + [securityfs]=1 + [sockfs]=1 + [spufs]=1 + [sysfs]=1 + [tmpfs]=1) + +# generated from: pkgfile -vbr '/fsck\..+' | awk -F. '{ print $NF }' | sort +declare -A fsck_types=([cramfs]=1 + [exfat]=1 + [ext2]=1 + [ext3]=1 + [ext4]=1 + [ext4dev]=1 + [jfs]=1 + [minix]=1 + [msdos]=1 + [reiserfs]=1 + [vfat]=1 + [xfs]=1) + +out() { printf "$1 $2\n" "${@:3}"; } +error() { out "==> ERROR:" "$@"; } >&2 +msg() { out "==>" "$@"; } +msg2() { out " ->" "$@";} +die() { error "$@"; exit 1; } + +ignore_error() { + "$@" 2>/dev/null + return 0 +} + +in_array() { + local i + for i in "${@:2}"; do + [[ $1 = "$i" ]] && return 0 + done + return 1 +} + +chroot_add_mount() { + mount "$@" && CHROOT_ACTIVE_MOUNTS=("$2" "${CHROOT_ACTIVE_MOUNTS[@]}") +} + +chroot_maybe_add_mount() { + local cond=$1; shift + if eval "$cond"; then + chroot_add_mount "$@" + fi +} + +chroot_setup() { + CHROOT_ACTIVE_MOUNTS=() + [[ $(trap -p EXIT) ]] && die '(BUG): attempting to overwrite existing EXIT trap' + trap 'chroot_teardown' EXIT + + chroot_maybe_add_mount "! mountpoint -q '$1'" "$1" "$1" --bind && + chroot_add_mount proc "$1/proc" -t proc -o nosuid,noexec,nodev && + chroot_add_mount sys "$1/sys" -t sysfs -o nosuid,noexec,nodev,ro && + ignore_error chroot_maybe_add_mount "[[ -d '$1/sys/firmware/efi/efivars' ]]" \ + efivarfs "$1/sys/firmware/efi/efivars" -t efivarfs -o nosuid,noexec,nodev && + chroot_add_mount udev "$1/dev" -t devtmpfs -o mode=0755,nosuid && + chroot_add_mount devpts "$1/dev/pts" -t devpts -o mode=0620,gid=5,nosuid,noexec && + chroot_add_mount shm "$1/dev/shm" -t tmpfs -o mode=1777,nosuid,nodev && + chroot_add_mount run "$1/run" -t tmpfs -o nosuid,nodev,mode=0755 && + chroot_add_mount tmp "$1/tmp" -t tmpfs -o mode=1777,strictatime,nodev,nosuid +} + +chroot_teardown() { + umount "${CHROOT_ACTIVE_MOUNTS[@]}" + unset CHROOT_ACTIVE_MOUNTS +} + +try_cast() ( + _=$(( $1#$2 )) +) 2>/dev/null + +valid_number_of_base() { + local base=$1 len=${#2} i= + + for (( i = 0; i < len; i++ )); do + try_cast "$base" "${2:i:1}" || return 1 + done + + return 0 +} + +mangle() { + local i= chr= out= + + unset {a..f} {A..F} + + for (( i = 0; i < ${#1}; i++ )); do + chr=${1:i:1} + case $chr in + [[:space:]\\]) + printf -v chr '%03o' "'$chr" + out+=\\ + ;; + esac + out+=$chr + done + + printf '%s' "$out" +} + +unmangle() { + local i= chr= out= len=$(( ${#1} - 4 )) + + unset {a..f} {A..F} + + for (( i = 0; i < len; i++ )); do + chr=${1:i:1} + case $chr in + \\) + if valid_number_of_base 8 "${1:i+1:3}" || + valid_number_of_base 16 "${1:i+1:3}"; then + printf -v chr '%b' "${1:i:4}" + (( i += 3 )) + fi + ;; + esac + out+=$chr + done + + printf '%s' "$out${1:i}" +} + +optstring_match_option() { + local candidate pat patterns + + IFS=, read -ra patterns <<<"$1" + for pat in "${patterns[@]}"; do + if [[ $pat = *=* ]]; then + # "key=val" will only ever match "key=val" + candidate=$2 + else + # "key" will match "key", but also "key=anyval" + candidate=${2%%=*} + fi + + [[ $pat = "$candidate" ]] && return 0 + done + + return 1 +} + +optstring_remove_option() { + local o options_ remove=$2 IFS=, + + read -ra options_ <<<"${!1}" + + for o in "${!options_[@]}"; do + optstring_match_option "$remove" "${options_[o]}" && unset 'options_[o]' + done + + declare -g "$1=${options_[*]}" +} + +optstring_normalize() { + local o options_ norm IFS=, + + read -ra options_ <<<"${!1}" + + # remove empty fields + for o in "${options_[@]}"; do + [[ $o ]] && norm+=("$o") + done + + # avoid empty strings, reset to "defaults" + declare -g "$1=${norm[*]:-defaults}" +} + +optstring_append_option() { + if ! optstring_has_option "$1" "$2"; then + declare -g "$1=${!1},$2" + fi + + optstring_normalize "$1" +} + +optstring_prepend_option() { + local options_=$1 + + if ! optstring_has_option "$1" "$2"; then + declare -g "$1=$2,${!1}" + fi + + optstring_normalize "$1" +} + +optstring_get_option() { + local opts o + + IFS=, read -ra opts <<<"${!1}" + for o in "${opts[@]}"; do + if optstring_match_option "$2" "$o"; then + declare -g "$o" + return 0 + fi + done + + return 1 +} + +optstring_has_option() { + local "${2%%=*}" + + optstring_get_option "$1" "$2" +} + +dm_name_for_devnode() { + read dm_name <"/sys/class/block/${1#/dev/}/dm/name" + if [[ $dm_name ]]; then + printf '/dev/mapper/%s' "$dm_name" + else + # don't leave the caller hanging, just print the original name + # along with the failure. + print '%s' "$1" + error 'Failed to resolve device mapper name for: %s' "$1" + fi +} + +fstype_is_pseudofs() { + (( pseudofs_types["$1"] )) +} + +fstype_has_fsck() { + (( fsck_types["$1"] )) +} + + +hostcache=0 +copykeyring=1 +copymirrorlist=1 + +usage() { + cat < create rootfs ${ROOTFS}" + mkdir -p $ROOTFS +} + +rootfs_mount() { + echo "==> mounting ${ROOTFS} as tmpfs" + ## safety first, make sure we do not mount rootfs recursively + umount -R "$ROOTFS" + mount -t tmpfs -o size=${ROOTFS_SIZE} tmpfs "$ROOTFS" +} + +rootfs_install() { + ${SCRIPTD}/pacstrap.sh -c -d "$ROOTFS" ${PKGS} + + if [[ ${#PKGS_ADD} > 0 ]]; then + ${SCRIPTD}/pacstrap.sh -c -d "$ROOTFS" ${PKGS_ADD} + fi +} + +rootfs_copy() { + echo "==> copying files ..." + + for k in "${(@k)FILES}"; do + echo " from $k to $FILES[$k]" + cp $k $FILES[$k] + done +} + +rootfs_bootstrap() { + RUN_BOOTSTRAP="${ROOTFS}/run_bootstrap.sh" + VAR_BOOTSTRAP="${ROOTFS}/vars.sh" + + echo "==> bootstraping ... ${RUN_BOOTSTRAP}" + + ## generate vars for bootstrap + echo '#!/bin/bash' > ${VAR_BOOTSTRAP} + echo "PKGS_REMOVED=($PKGS_REMOVED)" >> ${VAR_BOOTSTRAP} + + ## generate bootstrap script. + echo '#!/bin/bash' > ${RUN_BOOTSTRAP} + echo '. ./vars.sh' >> ${RUN_BOOTSTRAP} + + for (( i = 1; i <= ${#BOOTSTRAP_S}; i++ )) do + echo ". $BOOTSTRAP_S[$i]" >> ${RUN_BOOTSTRAP} + done + chmod +x ${RUN_BOOTSTRAP} + + ## run the bootstrap script. + arch-chroot "$ROOTFS" /bin/sh -c "/`basename ${RUN_BOOTSTRAP}`" +} + +## +## (1) set root fs. +## (2) create root fs directory. +## (3) mount root fs as tmpfs. +## (4) run pacstrap. +## (5) copy bootstrap script and default pacman config. +## (6) run bootstrap script in new root fs. +## +rootfs_main() { + rootfs_create + rootfs_mount + rootfs_install + rootfs_copy + rootfs_bootstrap +} + +## +## Convert rootfs to docker image. +## +rootfs_to_docker() { + if [[ $# < 2 ]]; then + echo "args: rootfs_to_docker [image-name]" + exit 1 + fi + + sudo tar --numeric-owner --xattrs --acls -C "$ROOTFS" -c . | + docker import ${@:2} - $1 +} + +## +## Unmount and remove rootfs. +## +rootfs_clean() { + sudo umount -R $ROOTFS + rm -f ${ROOTFS}/* + rmdir ${ROOTFS} +} -- cgit v1.3-6-g1900