#!/bin/bash # mkinitcpio - modular tool for building an init ramfs cpio image # # IMPORTANT: We need to keep a common base syntax here because # some of these hooks/scripts need to run under busybox's ash - # therefore, the following constraints should be enforced: # variables should be quoted and bracketed "${SOMEVAR}" # inline execution should be done with $() instead of backticks # use -z "${var}" to test for nulls/empty strings # in case of embedded spaces, quote all path names and string comparisons # shopt -s extglob # Settings TMPDIR="$(mktemp -d /tmp/mkinitcpio.XXXXXX)" BASEDIR="" MESSAGEFILE="${TMPDIR}/message" KERNELVERSION="$(uname -r)" FUNCTIONS="functions" CONFIG="mkinitcpio.conf" HOOKDIR="hooks" INSTDIR="install" MODULE_FILE="" SAVELIST="" GENIMG="" PRESET="" MESSAGE="" SKIPHOOKS=() PRESETDIR="mkinitcpio.d" QUIET="y" SHOW_AUTOMODS="n" COMPRESSION="gzip" COMPRESSION_OPTIONS="" if [ ! $UID ]; then UID=$(id -u) fi # Add /{,usr}/sbin to path # works around undetected problems like in #8448 PATH="${PATH}:/sbin:/usr/sbin" # Sanitize environment further # GREP_OPTIONS="--color=always" will break everything unset GREP_OPTIONS APPNAME=${0##*/} usage () { echo "${APPNAME}: usage" echo " -c CONFIG Use CONFIG file. default: /etc/mkinitcpio.conf" echo " -k KERNELVERSION Use KERNELVERSION. default: $(uname -r)" echo " -s Save build directory. default: no" echo " -b BASEDIR Use BASEDIR. default: /" echo " -g IMAGE Generate a cpio image as IMAGE. default: no" echo " -p PRESET Build specified preset." echo " -m MESSAGE Print MESSAGE before passing control to init." echo " -S SKIPHOOKS Skip SKIPHOOKS (comma-separated) when building the image." echo " -v Verbose output. Default: no" echo " -M Display modules found via autodetection." echo " -L List all available hooks." echo " -H HOOKNAME Output help for hook 'HOOKNAME'." echo " -h This message." cleanup exit 1 } cleanup () { if [[ $SAVELIST ]]; then echo ":: build directory saved in $TMPDIR" else rm -rf ${TMPDIR} fi } sighandler() { cleanup exit 1 } get_kernver() { local kernel=$1 if [[ "${kernel:0:1}" != / ]]; then echo $kernel return 0 fi [[ -r "$kernel" ]] || return 1 read _ kernver < <(file -b "$BASEDIR$kernel" | grep -o 'version [^ ]\+') if [[ "$kernver" && -e "$BASEDIR/lib/modules/$kernver" ]]; then echo "$kernver" return 0 fi return 1 } trap sighandler TERM INT while getopts ':c:k:sb:g:p:m:vH:LMhS:' arg; do if [ "${OPTARG#-}" != "${OPTARG}" ]; then echo "error: optional argument to '-${arg}' begins with a '-'" echo " you probably don't want this....aborting." usage fi case "${arg}" in c) CONFIG="${OPTARG}" ;; k) optkver=$OPTARG ;; s) SAVELIST="y"; ;; b) BASEDIR="${OPTARG}" ;; g) GENIMG="${OPTARG}" ;; p) PRESET="${OPTARG}" ;; m) MESSAGE="${OPTARG}" ;; v) QUIET="n" ;; S) OLDIFS=${IFS} IFS=, SKIPHOOKS=(${OPTARG}) IFS=${OLDIFS} unset OLDIFS ;; H) . "${INSTDIR}/${OPTARG}"; echo "Help for hook '${OPTARG}':" help cleanup exit 0 ;; L) echo "Available hooks: " for h in ${INSTDIR}/*; do echo " ${h##*/}" done cleanup exit 0 ;; M) SHOW_AUTOMODS="y" ;; h|?) usage ;; :) echo "${OPTARG} requires a value..."; usage ;; *) echo "invalid argument '${arg}'"; usage ;; esac done shift $((${OPTIND} - 1)) if [[ $optkver ]]; then if ! KERNELVERSION=$(get_kernver "$optkver"); then echo "error: '$optkver' is an invalid kernel specifier" cleanup exit 1 fi fi # use preset $PRESET if [[ $PRESET ]]; then if [[ -f "$PRESETDIR/$PRESET.preset" ]]; then # Use -b, -m and -v options specified earlier declare -a preset_mkopts preset_cmd [[ $BASEDIR ]] && preset_mkopts+=(-b "$BASEDIR") [[ $MESSAGE ]] && preset_mkopts+=(-m "$MESSAGE") [[ "$QUIET" = n ]] && preset_mkopts+=(-v) # Build all images . "$PRESETDIR/$PRESET.preset" for p in "${PRESETS[@]}"; do echo "==> Building image \"${p}\"" preset_cmd=("${preset_mkopts[@]}") preset_kver=${p}_kver if [[ ${!preset_kver:-$ALL_kver} ]]; then preset_cmd+=(-k "${!preset_kver:-$ALL_kver}") else echo "==> No kernel version specified. Skipping image \"${p}\"." continue fi preset_config=${p}_config if [[ ${!preset_config:-$ALL_config} ]]; then preset_cmd+=(-c "${!preset_config:-$ALL_config}") else echo "==> No configuration file specified. Skipping image \"${p}\"." continue fi preset_image=${p}_image if [[ ${!preset_image} ]]; then preset_cmd+=(-g "${!preset_image}") else echo "==> No image file specified. Skipping image \"${p}\"." continue fi preset_options=${p}_options if [[ ${!preset_options} ]]; then preset_cmd+=(${!preset_options}) # intentional word splitting fi echo "==> Running command: $0 ${preset_cmd[@]}" if "$0" "${preset_cmd[@]}"; then echo "==> SUCCESS" else echo "==> FAIL" fi done cleanup exit 0 else echo "Preset $PRESET does not exist. Exiting." cleanup exit 1 fi fi # remove trailing / from BASEDIR BASEDIR="${BASEDIR%/}" MODULEDIR="${BASEDIR}/lib/modules/${KERNELVERSION}" if [[ $GENIMG ]]; then IMGPATH=$(readlink -f "$GENIMG") if [[ -z $IMGPATH || ! -w ${IMGPATH%/*} ]]; then echo "error: unable to write to path: '$GENIMG'" cleanup exit 1 fi fi if [ -n "${BASEDIR}" ]; then if [ "${BASEDIR}" = "${BASEDIR#/}" ]; then BASEDIR="$(pwd)/${BASEDIR}" elif [ ! -d "${BASEDIR}" ]; then echo "base directory '${BASEDIR}' does not exist or is not a directory" cleanup exit 1 fi fi if [ ! -f "${CONFIG}" ]; then echo "config file '${CONFIG}' cannot be found, aborting..." cleanup exit 1 fi . "${CONFIG}" BASEDIR=${BASEDIR//+(\/)//} MODULEDIR=${MODULEDIR//+(\/)//} . "${FUNCTIONS}" if [ "${SHOW_AUTOMODS}" = "y" ]; then echo "Modules autodetected:" . "${INSTDIR}/autodetect" build cat "${MODULE_FILE}" cleanup exit 0 fi if [ -z "${GENIMG}" ]; then echo ":: Begin dry run" else echo ":: Begin build" fi #parse 'global' hook, as defined in ${CONFIG} parse_hook for hook in ${HOOKS}; do in_array "$hook" "${SKIPHOOKS[@]}" && continue unset MODULES BINARIES FILES SCRIPT build () { msg "${hook}: no build function..."; } # Deprecation check # A hook is considered deprecated if it is a symlink # within $INSTDIR. if [ -h "${INSTDIR}/${hook}" ]; then newhook="$(readlink -ne "${INSTDIR}/${hook}")" if [ -n "${newhook}" -a "${INSTDIR}/$(get_basename ${newhook})" -ef "${newhook}" ]; then newhook="$(get_basename ${newhook})" echo " -------------------------------------------------------------------" echo " WARNING: Hook \"${hook}\" is deprecated." echo " Replace it with \"${newhook}\" in your configuration file." echo " -------------------------------------------------------------------" hook="${newhook}" fi fi if [[ -r "${INSTDIR}/${hook}" ]]; then . "${INSTDIR}/${hook}" echo ":: Parsing hook [${hook}]" if [[ $(type -t install) = 'function' ]]; then echo " -----------------------------------------------------------------------" echo " WARNING: Hook '$hook' uses a deprecated 'install' function. This " echo " should be switched to a 'build' function instead." echo " -----------------------------------------------------------------------" install unset install else build fi parse_hook else die "Hook '${hook}' can not be found." fi done if (( ${#ADDED_MODULES[*]} )); then echo ":: Generating module dependencies" /sbin/depmod -b "${TMPDIR}/root" "${KERNELVERSION}" rm "$TMPDIR/root/lib/modules/$KERNELVERSION"/modules.!(dep.bin|alias.bin|symbols.bin) fi declare -i status=0 declare -a pipesave if [ -n "${GENIMG}" ]; then echo ":: Generating image '${GENIMG}'" [ ${COMPRESSION} = "xz" ] && COMPRESSION_OPTIONS="${COMPRESSION_OPTIONS} --check=crc32" pushd "$TMPDIR/root" >/dev/null find . -print0 | bsdcpio -0oH newc | $COMPRESSION $COMPRESSION_OPTIONS > "$IMGPATH" pipesave=("${PIPESTATUS[@]}") # save immediately popd >/dev/null if (( pipesave[0] )); then errmsg="find reported an error" elif (( pipesave[1] )); then errmsg="bsdcpio reported an error" elif (( pipesave[2] )); then errmsg="$COMPRESSION reported an error" fi if [[ $errmsg ]]; then echo ":: Image generation FAILED ($errmsg)" status=1 else echo ":: Image generation successful" fi else echo ":: Dry run complete, use -g IMAGE to generate a real image" fi cleanup exit ${status} #vim:set ft=sh ts=4 sw=4 noet: