#! /bin/bash # Default values. DEFAULT_MIRROR='http://hiciu.rootnode.net/arch-arm' # Command line options and default values. DEBUG='n' SUDO='y' HELP='n' BUILD='n' CHROOT='n' # ARCH can be specified on the command line. Otherwise we'll guess. ARCH='i686' # Which groups and additional packages are to install. TARGETS=() # PACMAN_EXTRA takes extra flags passed to pacman. No checking is done. PACMAN_EXTRA='' # Default values of command line arguments. DESTDIR='' MIRROR='' # Prints the help message, optionally using its first argument as script name. printHelp() { test -z "$1" && 1='archOnFr' echo "$1 [-a arch] [-s] [-n] [-d] [-t TARGETS] DESTDIR [MIRROR]" echo "$1 -b [-a arch] [-s] [-n] [-d] [-t TARGETS] DESTDIR [MIRROR]" echo "$1 -e [-s] [-d] DIR" echo "$1 -h" echo echo "In the first form $1 installs the base system suited to run on the smart phone" echo "to DESTDIR optionally using a different mirror for the packages instead of the" echo "default." echo echo "In the second form a build system is installed to DESTDIR optionally using a" echo "different mirror. The build system is suited to run on the host pc to develop" echo "Arch Mobile." echo echo "In the third form $1 enters the development environment given at DESTDIR." echo echo "In the fourth form this help message is printed." echo echo "OPTIONS" echo " -a --arch=[i686|x86_64]" echo " host system's architecture" echo " -d --debug" echo " print debug messages" echo " -h --help" echo " prints this help message" echo " -p --extra=flags" echo " add flags to pacman's flags when calling (use with care!)" echo " -s --nosudo" echo " don't use sudo for pacman calls (needs root previleges)" echo " -t --targets=TARGETS" echo " install TARGETS which might be groups or packages explicitly." echo " (Default: base)" echo echo "EXAMPLES" echo " $1 /media/card http://hiciu.rootnode.net/arch-arm" echo " $1 /media/card file:///home/harlekin/archpkgs" } # Check if we are already able to interpret ARM binaries and configure the # the kernel if we are not. Expects mount call and echo call as arguments. binfmt_mount() { MOUNT_CALL="$1" ECHO_CALL="$2" debug "Checking for binfmt_misc." if [ ! -e /proc/sys/fs/binfmt_misc/register ]; then debug "binfmt_misc not mounted." debug "$MOUNT_CALL -t binfmt_misc none /proc/sys/fs/binfmt_misc" $MOUNT_CALL -t binfmt_misc none /proc/sys/fs/binfmt_misc &>/dev/null test $? -ne 0 && \ error "Failed to mount binfmt_misc." fi if [ ! -f /proc/sys/fs/binfmt_misc/arm ]; then debug "$ECHO_CALL ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-static-arm:' > /proc/sys/fs/binfmt_misc/register" $ECHO_CALL ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-static-arm:' > /proc/sys/fs/binfmt_misc/register test $? -ne 0 && \ error "Failed to write ARM binary information to binfmt_misc/register." debug "binfmt_misc successfully created and configured." fi } debug() { test "$DEBUG" = "y" && echo "debug: $1" } error() { echo "error: $1" doCleanup exit 1 } # Script for cleaning up all temporary files and reverting temporary changes # (such as mounting) even if an error occurs. Takes an array as first argument # which consists of the affected file, prefixed with the action. # F:foo will remove the file foo # D:bar will remove the directory bar recursively # M:baz will un-mount bar doCleanup() { test ${#CLEANUP} -eq 0 && debug "Nothing to clean up." debug "Cleaning up..." for target in ${CLEANUP[*]}; do action="${target:0:2}" target="${target#?:}" case "$action" in F:) debug "Removing file $target." rm -f "$target" ;; D:) debug "Removing directory $target." rm -rf "$target" ;; M:) debug "Unmounting $target." if [ "$SUDO" = 'y' ]; then sudo umount "$target" else umount "$target" fi ;; esac done } # Print help if called without arguments and exit with error condition. test $# -eq 0 && HELP='y!' while [ $# -ne 0 ]; do case "$1" in --arch=*) ARCH="${1#--arch=}" ;; -a|--arch) shift ARCH="$1" ;; -b|--build) test $CHROOT = y && \ error "Command line option --build conflicts with --enter." BUILD='y' ;; -e|--enter) test $BUILD = y && \ error "Command line option --enter conflicts with --build." CHROOT='y' ;; -h|--help) HELP='y' ;; --extra=) PACMAN_EXTRA="${1#--extra=}" ;; -p|--extra) shift PACMAN_EXTRA="$1" ;; -d|--debug) DEBUG='y' ;; -s|--nosudo) SUDO='n' ;; --targets=) TARGETS=(${TARGETS[*]} "${1#--targets=}") ;; -t|--targets) shift TARGETS=(${TARGETS[*]} "$1") ;; -*) error "Unrecognized command line option $1." exit 1 ;; *) if [ -z "$DESTDIR" ]; then DESTDIR="$1" else if [ -z "$MIRROR" ]; then MIRROR="$1" else error "Unrecognized command line argument $1." fi fi ;; esac; shift done # Print help message if requested or set because of invalied command line # arguments. if [ "$HELP" = 'y!' ]; then printHelp "$(basename $0)" exit 1 elif [ "$HELP" = 'y' ]; then printHelp "$(basename $0)" exit 0 fi if [ -z "$ARCH" ]; then debug "ARCH = ?" else debug "ARCH = $ARCH" fi debug "BUILD = $BUILD" debug "FORCE = $FORCE" debug "NODEPS = $NODEPS" debug "SUDO = $SUDO" debug "DESTDIR = $DESTDIR" debug "CHROOT = $CHROOT" if [ -z "${TARGETS[*]}" ]; then debug "TARGETS = base" else debug "TARGETS = ${TARGETS[*]}" fi if [ -z "$MIRROR" ]; then debug "MIRROR = $DEFAULT_MIRROR" else debug "MIRROR = $MIRROR" fi debug "PATH = $PATH" debug "PACMAN_EXTRA = $PACMAN_EXTRA" # Check for root privileges test $SUDO = 'n' -a "$UID" -ne 0 && \ error "Need root privileges for pacman call or don't specify --nosudo." # If we are using sudo we should check if it is installed. debug "Checking for sudo." if [ $SUDO = y ]; then SUDO_CALL="$(which sudo 2>/dev/null)" test $? -ne 0 && \ error "sudo couldn't be found on your system. Please install and configure it or use --nosudo and run as root." fi debug "sudo found as $SUDO_CALL." # Set current working directory since we might change the directory STARTDIR="$(pwd)" test $? -ne 0 && error "Failed to obtain the current working directory." # Check if DESTDIR is set test -z "$DESTDIR" && error "DESTDIR not set." # Prepare mount, echo and chroot calls. MOUNT_CALL='mount' ECHO_CALL='echo' CHROOT_CALL='chroot' test $SUDO = y && MOUNT_CALL="$SUDO_CALL $MOUNT_CALL" test $SUDO = y && ECHO_CALL="$SUDO_CALL $ECHO_CALL" test $SUDO = y && CHROOT_CALL="$SUDO_CALL $CHROOT_CALL" # Entering buildsystem if [ $CHROOT = y ]; then debug "Entering build system." binfmt_mount "$MOUNT_CALL" "$ECHO_CALL" debug "$CHROOT_CALL $DESTDIR /bin/bash" $CHROOT_CALL $DESTDIR /bin/bash test $? -ne 0 && \ error "Failed to chroot into build systen in $DESTDIR." exit 0 fi # Set default mirror if MIRROR isn't set test -z "$MIRROR" && MIRROR="$DEFAULT_MIRROR" # If no targets are specified we default to the base group and the stock kernel. test -z "${TARGETS[*]}" && TARGETS=(base kernel26) # If we are installing the build environment we need to know the host system's # architecture. test -z "$ARCH" && ARCH="$(arch 2>/dev/null)" test -z "$ARCH" && \ error "Could not obtain architecture type. Please use --arch manually." if [ "$ARCH" != 'i686' -a "$ARCH" != 'x86_64' ]; then error "Host system's architecture $ARCH is not supported" fi # Prepending the current working directory if DESTDIR isn't abolute. Safety # measure as we're going to change to a temporary working directory. if [ "${DESTDIR:0:1}" != "/" ]; then debug 'DESTDIR not an absolute path. Prepending STARTDIR.' DESTDIR="$STARTDIR/$DESTDIR" fi # Remove trailing / of MIRROR if necessary MIRROR="${MIRROR%/}" # Create DESTDIR if it doesn't exist yet. if [ ! -d "$DESTDIR" ]; then debug "Destination installation directory does not exist." debug "Creating destination directory." debug "mkdir -p \"$DESTDIR\" 2>/dev/null" mkdir -p "$DESTDIR" 2>/dev/null if [ $? -ne 0 ]; then error "Failed to create $DESTDIR. Please create it manually and run again." fi fi # Trying to find a pacman executable. debug "Trying to find a pacman binary." PACMAN="$(which pacman.static 2>/dev/null)" if [ $? -ne 0 ]; then debug "No pacman.static available in PATH." PACMAN='' fi if [ -z "$PACMAN" ]; then PACMAN="$(which pacman-static 2>/dev/null)" if [ $? -ne 0 ]; then debug "No pacman-static available in PATH." PACMAN='' fi fi if [ -z "$PACMAN" ]; then PACMAN="$(which pacman 2>/dev/null)" if [ $? -ne 0 ]; then debug "No pacman available in PATH." PACMAN='' fi fi # Check if pacman is now available test -z "$PACMAN" && \ error "Could not obtain pacman binary. Install pacman to your PATH and run again." debug "Found pacman binary as $PACMAN." # Creating temporary pacman.conf debug "Creating temporary pacman.conf." CONFIG="$(mktemp -t pacman.conf.XXXX 2>/dev/null)" test $? -ne 0 && CONFIG='' test -z "$CONFIG" && error "Failed to create temporary pacman.conf." # Build up pacman's temporary configuration file. We need the build repository # even when we're not perfoming a build environment installation because of the # system dependent qemu. CLEANUP=("F:$CONFIG" ${CLEANUP[*]}) echo "[build]" > "$CONFIG" echo "Server = $MIRROR/$ARCH/build" >> "$CONFIG" echo >> "$CONFIG" echo "[core]" >> "$CONFIG" echo "Server = $MIRROR/arm/core" >> "$CONFIG" MKDIR_CALL='mkdir' test "$SUDO" = 'y' && MKDIR_CALL="$SUDO_CALL $MKDIR_CALL" # Mounting binfmt if necessary. binfmt_mount "$MOUNT_CALL" "$ECHO_CALL" # Mounting proc and dev. This is needed for executing scriptlets using qemu. for dir in proc dev; do debug "Creating $dir directory under $DESTDIR." debug "$MKDIR_CALL -p \"$DESTDIR/$dir\" &>/dev/null" $MKDIR_CALL -p "$DESTDIR/$dir" &>/dev/null test $? -ne 0 && error "Failed to create $dir directory under $DESTDIR." done for dir in proc dev; do debug "Mounting $dir under new root." debug "$MOUNT_CALL -o bind /$dir \"$DESTDIR/$dir\" &>/dev/null" $MOUNT_CALL -o bind /$dir "$DESTDIR/$dir" &>/dev/null test $? -ne 0 && error "Failed to bind $dir to $DESTDIR/$dir." CLEANUP=("M:$DESTDIR/$dir" ${CLEANUP[*]}) done # Set correct umask debug "Setting umask to 022." umask 022 &>/dev/null test $? -ne 0 && error "Failed to set umask to 022." # Creating pacman's working directory debug "Creating pacman's working directory." debug "$MKDIR_CALL -p \"$DESTDIR/var/lib/pacman\" &>/dev/null" $MKDIR_CALL -p "$DESTDIR/var/lib/pacman" &>/dev/null test $? -ne 0 && error "Failed to create pacman's working directory." # Preparing pacman call PACMAN_FLAGS="-r $DESTDIR --config $CONFIG --noconfirm --noprogressbar" PACMAN_TARGETS="${TARGETS[*]}" PACMAN_CALL="$PACMAN" test "$SUDO" = 'y' && PACMAN_CALL="$SUDO_CALL $PACMAN_CALL" test "$BUILD" = 'y' && PACMAN_TARGETS="base-devel $PACMAN_TARGETS" PACMAN_FLAGS="$PACMAN_FLAGS $PACMAN_EXTRA" debug "PACMAN_FLAGS = $PACMAN_FLAGS" debug "PACMAN_TARGETS = $PACMAN_TARGETS" debug "${CONFIG}:" cat "$CONFIG" | while read ln; do debug "$ln" done # Installing qemu (for chrooting) and gcc-libs (for shared libs) debug "Installing qemu and gcc-libs." debug "$PACMAN_CALL $PACMAN_FLAGS -Sy --noscriptlet --nodeps qemu gcc-libs" $PACMAN_CALL $PACMAN_FLAGS -Sy --noscriptlet --nodeps qemu gcc-libs test $? -ne 0 && error "Failed to install qemu or gcc-libs." # Installing base and possibly base-devel. debug "Installing base system." debug "$PACMAN_CALL $PACMAN_FLAGS -Sy $PACMAN_TARGETS" $PACMAN_CALL $PACMAN_FLAGS -Sy $PACMAN_TARGETS test $? -ne 0 && error "Failed to install base or possibly base-devel." # Removing qemu again if necessary. if [ "$BUILD" != 'y' ]; then debug "Removing qemu from new root." debug "$CHROOT_CALL $DESTDIR pacman -Rns --noconfirm --noprogressbar qemu" $CHROOT_CALL $DESTDIR pacman -Rns --noconfirm --noprogressbar qemu test $? -ne 0 && error "Failed to remove qemu." fi doCleanup exit 0 # vim: set ft=sh ts=2 sw=2 et: