#!/bin/bash msg () { [ "${QUIET}" = "n" ] && echo $@; } err () { echo "ERROR: $@" >&2; } die () { echo "FATAL: $@" >&2; cleanup; exit 1; } get_dirname() { # strip any trailing slash first... local dir="${1%/}" # then get the directory portion echo "${dir%/*}" } get_basename() { echo "${1##*/}" } get_module_name() { #cleanup - remove .ko, replace - with _ local modname="${1%.gz}" modname=$(get_basename "${modname%.ko}") modname="${modname//-/_}" echo "$modname" } in_array() { # Search for an element in an array. # $1: needle # ${@:2}: haystack local needle=$1; shift [[ -z $1 ]] && return 1 # Not Found local item for item in "$@"; do [[ $item = $needle ]] && return 0 # Found done return 1 # Not Found } kmodinfo() { # A wrapper around modinfo, which is aware of our $BASEDIR and # $KERNELVERSION # $@: args to modinfo modinfo -b "$BASEDIR" -k "$KERNELVERSION" "$@" 2>/dev/null } auto_modules() { # Perform auto detection of modules via sysfs. IFS=$'\n' read -rd '' -a mods < \ <(find /sys/devices -name modalias -exec sort -zu {} + | xargs -0 modprobe -aRS "$KERNELVERSION" | sort -u) printf "%s\n" "${mods[@]//-/_}" (( ${#mods[*]} )) } all_modules() { # Add modules to the initcpio, filtered by grep. # $@: filter arguments to grep local -i count=0 while read -r -d '' mod; do (( ++count )) mod=${mod##*/} mod="${mod%.ko*}" printf '%s\n' "${mod//-/_}" done < <(find "$MODULEDIR" -name '*.ko*' -print0 2>/dev/null | grep -Zz "$@") (( count )) } checked_modules() { # Add modules to the initcpio, filtered by the list of autodetected # modules. # $@: arguments to all_modules if [[ -s "$MODULE_FILE" ]]; then grep -xFf "$MODULE_FILE" <(all_modules "$@") return 1 else all_modules "$@" fi } add_full_dir() { # Add a directory and all its contents, recursively, to the initcpio image. # No parsing is performed and the contents of the directory is added as is. # $1: path to directory if [[ -n $1 && -d $1 ]]; then for f in "$1"/*; do if [[ -d "$f" ]]; then add_full_dir "$f" else add_file "$f" fi done fi } add_dir() { # Add a directory to the initcpio image. # $1: absolute path of directory on image if [[ ! -e "$TMPDIR/root/$1" ]]; then msg " adding dir ${1}" command install -dm755 "$TMPDIR/root/$1" fi } add_symlink() { # Add a symlink to the initcpio image. # $1: pathname of symlink on image # $2: absolute path to target of symlink local fil dest dir if [[ -h $1 ]]; then fil="${1##$BASEDIR}" dest="${2##$BASEDIR}" add_dir $(get_dirname "${dest}") add_dir $(get_dirname "${fil}") if [[ ! -e "$TMPDIR/root/$dest" ]]; then msg " adding link ${fil} -> ${dest}" ln -s "$dest" "$TMPDIR/root/$fil" fi fi #fail quietly } add_file() { # Add a plain file to the initcpio image. No parsing is performed and only # the singular file is added. # $1: path to file # $2: destination on initcpio (optional, defaults to same as source) local file dest file=$1 dest=${2:-${file##$BASEDIR}} if [[ -f "$file" ]]; then if [[ -L "$file" ]]; then add_symlink "$file" "$(readlink -f "$file")" add_file "$(readlink -f "$file")" return fi if [[ ! -e "$TMPDIR/root/$dest" ]]; then msg " adding file $dest" command install -Dm$(stat -c '%a' "$file") "$file" "$TMPDIR/root/$dest" fi else err "file '$file' does not exist" return 1 fi } declare -a ADDED_MODULES add_module() { # Add a kernel module to the initcpio image. Dependencies will be # discovered and added. # $1: module name local module path fw dep deps module=${1%.ko*} #skip expensive stuff if this module has already been added if in_array $module ${ADDED_MODULES[@]}; then msg "module $module was already added" return fi path=$(kmodinfo -0F filename "$module") if [[ $path ]]; then # get module firmware while read -r -d '' fw; do if [[ -e "$BASEDIR/lib/firmware/$fw" ]]; then add_file "$BASEDIR/lib/firmware/$fw" fi done < <(kmodinfo -0F firmware "$module") # get module depends IFS=',' read -r -d '' -a deps < <(kmodinfo -0F depends "$module") for dep in "${deps[@]}"; do add_module "$dep" done ADDED_MODULES+=("$module") msg " adding module $module" add_file "$path" || return else err "module '$module' not found" return fi # explicit module depends case "$module" in fat) add_module "nls_cp437" ;; ocfs2) add_module "configfs" ;; libcrc32c) add_module "crc32c"; add_module "crc32c_intel" ;; esac } add_binary() { # add a binary file to the initcpio image. library dependencies will # be discovered and added. # $1: path to binary # $2: destination on initcpio (optional, defaults to same as source) local bin dest lib bin=$(type -P "$1") dest=$2 if [[ ! -f "$bin" ]]; then err "'$1' is not a file" return 1 fi case "$(file -b "$bin")" in *script*) msg " adding '${type}' script, ensure proper interp exists..." add_file "$bin" ${dest+"$dest"} ;; *executable*|*shared\ object*|*symbolic\ link*) add_file "$bin" ${dest+"$dest"} # note, this will also handle 'not a dynamic executable' spit out # by static binaries... the deps will produce nothing while read -r lib; do [[ -f "$lib" ]] && add_file "$lib" done < <(ldd "$bin" 2>/dev/null | sed '1d;s|.*=>\(.*\)|\1|;s/(0x[0-9a-f]\+)//') ;; *) err "unknown type '$type' for binary '$bin'" return 1 ;; esac } parse_hook() { # parse key global variables set by install hooks. This function is called # prior to the start of hook processing, and after each hook's build # function is run. local mod bin fil for mod in ${MODULES}; do if [ -n "${mod}" ]; then add_module "${mod}" fi done for bin in ${BINARIES}; do if [ -n "${bin}" ]; then add_binary "${bin}" fi done for fil in ${FILES}; do if [ -n "${fil}" ]; then add_file "${fil}" fi done if [ -n "${SCRIPT}" ]; then add_file "${HOOKDIR}/${SCRIPT}" "/hooks/${SCRIPT}" fi } # vim: set ft=sh ts=4 sw=4 et: