#!/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) MESSAGEFILE=$TMPDIR/message KERNELVERSION=$(uname -r) FUNCTIONS=functions CONFIG=mkinitcpio.conf HOOKDIR=hooks INSTDIR=install PRESETDIR=mkinitcpio.d COMPRESSION=gzip declare BASEDIR MODULE_FILE SAVELIST GENIMG PRESET MESSAGE COMPRESSION_OPTIONS declare NC= BOLD= BLUE= GREEN= RED= YELLOW= declare -i QUIET=1 SHOW_AUTOMODS=0 SAVELIST=0 COLOR=1 declare -a SKIPHOOKS ADDED_MODULES # 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 " -n Disable colorized output messages." 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 msg "build directory saved in %s" "$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 } . "$FUNCTIONS" trap sighandler TERM INT while getopts ':c:k:sb:g:p:m:nvH:LMhS:' arg; do case "${arg}" in c) CONFIG="${OPTARG}" ;; k) optkver=$OPTARG ;; s) SAVELIST=1; ;; b) BASEDIR="${OPTARG}" ;; g) GENIMG="${OPTARG}" ;; p) PRESET="${OPTARG}" ;; m) MESSAGE="${OPTARG}" ;; n) COLOR=0 ;; v) QUIET=0 ;; S) OLDIFS=${IFS} IFS=, SKIPHOOKS=(${OPTARG}) IFS=${OLDIFS} unset OLDIFS ;; H) . "${INSTDIR}/${OPTARG}"; msg "Help for hook '${OPTARG}'" help cleanup exit 0 ;; L) msg "Available hooks" for h in ${INSTDIR}/*; do echo " ${h##*/}" done cleanup exit 0 ;; M) SHOW_AUTOMODS=1 ;; h|?) usage ;; :) echo "${OPTARG} requires a value..."; usage ;; esac done shift $((${OPTIND} - 1)) if [[ -t 2 ]] && (( COLOR )); then # prefer terminal safe colored and bold text when tput is supported if tput setaf 0 &>/dev/null; then NC="$(tput sgr0)" BOLD="$(tput bold)" BLUE="$BOLD$(tput setaf 4)" GREEN="$BOLD$(tput setaf 2)" RED="$BOLD$(tput setaf 1)" YELLOW="$BOLD$(tput setaf 3)" else NC="\e[1;0m" BOLD="\e[1;1m" BLUE="$BOLD\e[1;34m" GREEN="$BOLD\e[1;32m" RED="$BOLD\e[1;31m" YELLOW="$BOLD\e[1;33m" fi fi readonly NC BOLD BLUE GREEN RED YELLOW if [[ $optkver ]]; then if ! KERNELVERSION=$(get_kernver "$optkver"); then die "'$optkver' is an invalid kernel specifier" 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 )) || preset_mkopts+=(-v) # Build all images . "$PRESETDIR/$PRESET.preset" for p in "${PRESETS[@]}"; do msg "Building image from preset: '$p'" preset_cmd=("${preset_mkopts[@]}") preset_kver=${p}_kver if [[ ${!preset_kver:-$ALL_kver} ]]; then preset_cmd+=(-k "${!preset_kver:-$ALL_kver}") else warning "No kernel version specified. Skipping image '%s'" "$p" continue fi preset_config=${p}_config if [[ ${!preset_config:-$ALL_config} ]]; then preset_cmd+=(-c "${!preset_config:-$ALL_config}") else warning "No configuration file specified. Skipping image '%s'" "$p" continue fi preset_image=${p}_image if [[ ${!preset_image} ]]; then preset_cmd+=(-g "$BASEDIR${!preset_image}") else warning "No image file specified. Skipping image '%s'" "$p" continue fi preset_options=${p}_options if [[ ${!preset_options} ]]; then preset_cmd+=(${!preset_options}) # intentional word splitting fi msg2 "${preset_cmd[*]}" "$0" "${preset_cmd[@]}" done cleanup exit 0 else die "Preset $PRESET does not exist. Exiting." fi fi if [[ $BASEDIR ]]; then # resolve the path. it might be a relative path and/or contain symlinks abspath=$(readlink -e "$BASEDIR") if [[ -z $abspath ]]; then die "base directory '$BASEDIR' does not exist or is not a directory" fi BASEDIR=$abspath unset abspath fi if [[ $GENIMG ]]; then IMGPATH=$(readlink -f "$GENIMG") if [[ -z $IMGPATH || ! -w ${IMGPATH%/*} ]]; then die "error: unable to write to path: '$GENIMG'" fi fi if [[ ! -f "$CONFIG" ]]; then die "config file '$CONFIG' cannot be found, aborting..." fi . "$CONFIG" MODULEDIR=$BASEDIR/lib/modules/$KERNELVERSION if [[ ! -d $MODULEDIR ]]; then die "'$MODULEDIR' is not a valid kernel module directory" fi if (( SHOW_AUTOMODS )); then msg "Modules autodetected" . "${INSTDIR}/autodetect" build cat "${MODULE_FILE}" cleanup exit 0 fi if [ -z "${GENIMG}" ]; then msg "Starting dry run: %s" "$KERNELVERSION" else msg "Starting build: %s" "$KERNELVERSION" 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 () { error "$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})" warning "Hook '%s' is deprecated. Replace it with '%s' in your config" "$hook" "$newhook" hook="${newhook}" fi fi if [[ -r "${INSTDIR}/${hook}" ]]; then . "${INSTDIR}/${hook}" msg2 "Parsing hook: [%s]" "$hook" if [[ $(type -t install) = 'function' ]]; then warning "Hook '%s' uses a deprecated 'install' function. This should be renamed 'build'" "$hook" install unset install else build fi parse_hook else die "Hook '$hook' can not be found." fi done if (( ${#ADDED_MODULES[*]} )); then msg "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 msg "Creating $COMPRESSION initcpio image: %s" "$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 error "Image generation FAILED: %s\n" "$errmsg" status=1 else msg "Image generation successful" fi else msg "Dry run complete, use -g IMAGE to generate a real image" fi cleanup exit ${status} # vim: set ft=sh ts=4 sw=4 et: