#!/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 # declare -r version=%VERSION% shopt -s extglob # Settings FUNCTIONS=functions CONFIG=mkinitcpio.conf HOOKDIR=hooks INSTDIR=install PRESETDIR=mkinitcpio.d COMPRESSION=gzip declare BASEDIR= MODULE_FILE= GENIMG= PRESET= COMPRESSION_OPTIONS= BUILDROOT= declare NC= BOLD= BLUE= GREEN= RED= YELLOW= declare -i QUIET=1 SHOW_AUTOMODS=0 SAVELIST=0 COLOR=1 declare -a SKIPHOOKS ADDED_MODULES MODPATHS # 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 # CDPATH can affect cd and pushd unset GREP_OPTIONS CDPATH usage() { cat </dev/null; printf ' %s\n' * ) done | column -c$(tput cols) exit 0 ;; M) SHOW_AUTOMODS=1 ;; t) TMPDIR=$OPTARG ;; z) optcompress=$OPTARG ;; :) error "option requires an argument -- '$OPTARG' (use -h for help)" exit 1 ;; \?) error "invalid option -- '$OPTARG' (use -h for help)" exit 1 ;; esac done shift $((OPTIND - 1)) if [[ -t 1 ]] && (( 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 # insist that /proc and /dev be mounted (important for chroots) # NOTE: avoid using mountpoint for this -- look for the paths that we actually # use in mkinitcpio. Avoids issues like FS#26344. [[ -e /proc/self/mountinfo ]] || die "/proc must be mounted!" [[ -e /dev/fd ]] || die "/dev must be mounted!" if [[ $BASEDIR ]]; then # resolve the path. it might be a relative path and/or contain symlinks if ! pushd "$BASEDIR" &>/dev/null; then die "base directory does not exist or is not a directory: \`%s'" "$BASEDIR" fi BASEDIR=$(pwd -P) BASEDIR=${BASEDIR%/} popd &>/dev/null fi KERNELVERSION=$(get_kernver "$optkver") || cleanup 1 if [[ $TMPDIR ]]; then if [[ ! -d $TMPDIR ]]; then error "Temporary directory does not exist or is not a directory: \`%s'" "$TMPDIR" cleanup 1 fi if [[ ! -w $TMPDIR ]]; then error "Temporary directory is not writeable: \`%s'" "$TMPDIR" cleanup 1 fi fi workdir=$(TMPDIR=$TMPDIR mktemp -d --tmpdir mkinitcpio.XXXXXX) BUILDROOT=$workdir/root # explicitly create the buildroot mkdir -p "$BUILDROOT/lib/modules/$KERNELVERSION/kernel" # use preset $PRESET if [[ $PRESET ]]; then # allow absolute path to preset file, else resolve it if [[ "${PRESET:0:1}" != '/' ]]; then printf -v PRESET '%s/%s.preset' "$PRESETDIR" "$PRESET" fi if [[ -f "$PRESET" ]]; then # Use -b, -m and -v options specified earlier declare -a preset_mkopts preset_cmd [[ $BASEDIR ]] && preset_mkopts+=(-b "$BASEDIR") (( QUIET )) || preset_mkopts+=(-v) # Build all images . "$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 "$BASEDIR${!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 0 else die "Preset not found: \`%s'" "$PRESET" fi fi if [[ $GENIMG ]]; then IMGPATH=$(readlink -f "$GENIMG") if [[ -z $IMGPATH || ! -w ${IMGPATH%/*} ]]; then die "Unable to write to path: \`%s'" "$GENIMG" fi fi if [[ ! -f "$CONFIG" ]]; then die "Config file does not exist: \`%s'" "$CONFIG" 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" autodetect=$(find_in_dirs 'autodetect' "${INSTDIR[@]}") . "$autodetect" build cat "$MODULE_FILE" cleanup 0 fi if [[ -z $GENIMG ]]; then msg "Starting dry run: %s" "$KERNELVERSION" else COMPRESSION=${optcompress:-$COMPRESSION} if ! type -P "$COMPRESSION" >/dev/null; then die "Unable to locate compression method: %s" "$COMPRESSION" fi msg "Starting build: %s" "$KERNELVERSION" fi # set errtrace and a trap to catch errors in parse_hook declare -i builderrors=0 set -E trap '[[ $FUNCNAME = parse_hook ]] && (( ++builderrors ))' ERR # save vars from $CONFIG; they will be parsed last for var in MODULES BINARIES FILES; do declare "cfg_$var=${!var}" done for hook in ${HOOKS} "${ADDHOOKS[@]}"; do in_array "$hook" "${SKIPHOOKS[@]}" && continue unset MODULES BINARIES FILES SCRIPT build() { error "$hook: no build function..."; return 1; } # find script in install dirs if ! script=$(find_in_dirs "$hook" "${INSTDIR[@]}"); then error "Hook '$hook' cannot be found." (( ++builderrors )) continue fi # check for deprecation if [[ -L $script ]]; then if ! realscript=$(readlink -e "$script"); then error "$script is a broken symlink to $(realpath "$script")" (( ++builderrors )) continue fi warning "Hook '%s' is deprecated. Replace it with '%s' in your config" "$script" "$realscript" script=$realscript fi # source if ! . "$script"; then error 'Failed to read %s' "$script" (( ++builderrors )) continue fi # run msg2 "Parsing hook: [%s]" "${script##*/}" build parse_hook done # restore $CONFIG vars add to image for var in cfg_{MODULES,BINARIES,FILES}; do declare "${var#cfg_}=${!var}" done parse_hook # reset the trap to catch all errors trap '(( ++builderrors ))' ERR if (( ${#ADDED_MODULES[*]} )); then printf '%s\0' "${MODPATHS[@]}" | sort -zu | xargs -0 cp -t "$BUILDROOT/lib/modules/$KERNELVERSION/kernel" # unzip modules prior to recompression gzip -dr "$BUILDROOT/lib/modules/$KERNELVERSION/kernel" msg "Generating module dependencies" install -m644 -t "$BUILDROOT/lib/modules/$KERNELVERSION" \ "$BASEDIR/lib/modules/$KERNELVERSION"/modules.{builtin,order} depmod -b "$BUILDROOT" "${KERNELVERSION}" # remove all non-binary module.* files (except devname for on-demand module loading) rm "$BUILDROOT/lib/modules/$KERNELVERSION"/modules.!(*.bin|devname) else warning "No modules were added to the image. This is probably not what you want." fi # unset errtrace and trap set +E trap ERR declare -i status=0 declare -a pipesave if [[ "${GENIMG}" ]]; then msg "Creating $COMPRESSION initcpio image: %s" "$GENIMG" [[ "$COMPRESSION" = xz ]] && COMPRESSION_OPTIONS+=" --check=crc32" # write version stamp printf '%s' "$version" > "$BUILDROOT/VERSION" pushd "$BUILDROOT" >/dev/null find . -print0 | bsdcpio $( (( QUIET )) && echo '--quiet' ) -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 (( builderrors )); then warning "errors were encountered during the build. The image may not be complete." status=1 fi if [[ $errmsg ]]; then error "Image generation FAILED: %s" "$errmsg" status=1 else msg "Image generation successful" fi else msg "Dry run complete, use -g IMAGE to generate a real image" fi cleanup $status # vim: set ft=sh ts=4 sw=4 et: