#!/usr/bin/env bash # -*- coding: utf-8 -*- # region header # Copyright Torben Sickert (info["~at~"]torben.website) 29.10.2015 # Janosch Dobler (info["~at~"]jandob.com) 29.10.2015 # Jonathan Bauer (jonathan.bauer@rz.uni-freiburg.de) 19.09.2019 # Thiago Abdo (tjabdo@inf.ufpr.br) 06.11.2019 # License # ------- # This library written by Torben Sickert and Janosch Dobler stand under a # creative commons naming 3.0 unported license. # see http://creativecommons.org/licenses/by/3.0/deed.de ## endregion # Afaik the idea here was to have the root dir where build-initramfs.sh # is called from and the repo dir where the systemd-init.git is cloned to: # # |- build-initramfs.sh # |- dracut # |- systemd-init declare -rg _root_dir="$(readlink -f $(dirname ${BASH_SOURCE[0]}))" # TODO clean these up file_path='/boot/initramfs.img' dracut_parameter=(--force --no-hostonly) verbose='no' debug='no' target='' cleanup='no' full_cleanup='no' use_systemd_in_initramfs='no' declare -rg _bootstrap_modules="bootstrap.conf" bootstrap() { if [ ! -f "$_bootstrap_modules" ]; then echo "Missing modules definition file: $_bootstrap_modules" print_help_message 1 exit 1 fi . "$_bootstrap_modules" if [[ ! -v core_dracut[@] ]] || [[ ! -v core_repo[@] ]]; then echo "Missing core modules for dracut & systemd-init" exit 1 fi declare -rg _repo_dir="$(readlink -f ${core_repo[path]})" declare -rg _dracut_dir="$(readlink -f ${core_dracut[path]})" echo "Modules file: $_bootstrap_modules" echo "Core modules: ${!core_*}" echo "Modules: ${!module_*}" echo "" for module in "${!core_@}" "${!module_@}"; do declare -n _ref="$module" if [ -n "$(ls -A "${_ref[path]}" 2> /dev/null)" ]; then echo "'${_ref[path]}' not empty, skipping..." continue fi echo "######################### $module #########################" echo "Handler: ${_ref[handler]}" echo " URL: ${_ref[url]}" echo " Branch: ${_ref[branch]}" echo " Commit: ${_ref[commit]}" echo " Path: ${_ref[path]}" handler_${_ref[handler]} \ "${_ref[path]}" \ "${_ref[url]}" \ "${_ref[branch]}" \ "${_ref[commit]}" # apply patches if any are required shopt -s nullglob pushd "${_ref[path]}" for patch in "${_repo_dir}/patches/${_ref[path]##*/}/"*.patch; do patch -p1 < "$patch" done popd done } handler_git() { local path="$1" local url="$2" local branch="$3" local commit="$4" mkdir -p "$path" local gitargs=( \ "--depth" "1" \ "--single-branch" \ ) if [ -n "$branch" ]; then gitargs+=("--branch" "$branch") fi if ! git clone "${gitargs[@]}" "$url" "$path"; then echo "Error cloning '$url' to '$path'." exit 1 fi [ -z "$commit" ] && return 0 # make sure given commit in in the fetched history local revision="$branch" if [ -n "$commit" ]; then revision="$commit" fi # manual "shallow clone" since not all server allow it... pushd "$path" local i=50 while ! git rev-parse --quiet --verify $revision^{commit}; do git fetch --depth=$(( i+=50 )) done git reset --hard "$revision" popd } handler_http() { local path="$1" local url="$2" mkdir --parents "$path" curl \ --location \ --max-redirs 5 \ --max-time 7 \ --connect-timeout 2 \ --retry 3 \ --retry-max-time 12 \ "$url" \ | tar \ --extract \ --gzip \ --directory "$path" \ --strip-components 1 } print_help_message() { echo "TODO" } parse_command_line() { while true; do case "$1" in -h|--help) shift print_help_message "$0" exit 0 ;; -v|--verbose) shift verbose='yes' ;; -d|--debug) shift debug='yes' ;; -p|--file-path) local given_argument="$1" shift file_path="$1" if [[ "$file_path" == '' ]]; then echo \ "Error with given option \"$given_argument\":" \ "This option needs a path to save initramfs image to." return 1 fi shift ;; -c|--cleanup) shift cleanup='yes' ;; -f|--full-cleanup) shift full_cleanup='yes' ;; -s|--use-systemd-in-initramfs) shift use_systemd_in_initramfs='yes' ;; -t|--target) local given_argument="$1" shift target="$1" if [[ "$target" == '' ]]; then echo \ "Error with given option \"$given_argument\":" \ "This option needs a path create initramfs from." return 1 fi shift ;; -i|--init) shift initialize='yes' ;; -k|--kernel-version) local given_argument="$1" shift kernel_version="$1" if [ -z "$kernel_version" ]; then echo \ "Error with given option \"$given_argument\":" \ "This option needs a kernel version to build the initramfs for." return 1 fi shift ;; -H|--kernel-headers) local given_argument="$1" shift kernel_headers="$1" if [ -z "$kernel_headers" ]; then echo \ "Error with given option \"$given_argument\":" \ "This option needs the path to the kernel headers." return 1 fi shift ;; -q|--qcow-handler) local given_argument="$1" shift qcow_handler="$1" if [ -z "$qcow_handler" ]; then echo \ "Error with given option \"$given_argument\":" \ "This options needs to be either 'xmount' or 'kernel'." return 1 fi shift ;; -u|--update) shift update='yes' ;; -) shift while [[ "$1" =~ ^.+$ ]]; do dracut_parameter+=("$1") shift done ;; '') break ;; *) echo \ "Error with given option \"$1\": This argument is not available." return 1 esac done return 0 } ## endregion ## region helper initialize_dracut() { pushd "${_dracut_dir}" # NOTE: On virtualbox shared folder symlinks are not allowed. # NOTE: make the dracut-install binary (dracut-install resolves # dependencies etc.) echo 'Compiling dracut.' ./configure make install/dracut-install # NOTE: We have to copy the binary to current instead of symlinking # them since this feature isn't supported in shared virtual box machine # folders. # If symlinks would be available we could simply use: # >>> make dracut-install popd cp "${_dracut_dir}/install/dracut-install" \ "${_dracut_dir}/dracut-install" return $? } # The idea here was to source each module-setup.sh from our # custom dracut modules and call their clean() function. # TODO: Does this still work? cleanup() { local plugin_path plugin_path="${_repo_dir}/modules.d/dnbd3-rootfs" source "${plugin_path}/module-setup.sh" moddir="$(cd "$plugin_path" &>/dev/null && pwd)" clean return $? } main() { if ! parse_command_line "$@"; then print_help_message "$0" exit 1 fi # prepare remote modules and set core variables. bootstrap if [ "$full_cleanup" = "yes" ]; then echo 'Removing all modules.' rm "${_repo_dir}" "${_dracut_dir}" --recursive --force exit $? fi if [ "$cleanup" = "yes" ]; then echo 'Removing distribution specific files.' cleanup exit $? fi # Default to the running kernel's version if none were specified echo "Building for:" if [ -z "$kernel_version" ]; then kernel_version="$(uname -r)" fi echo " * kernel version: $kernel_version" # similar for kernel headers needed to compile dnbd3 against. if [ -z "$kernel_headers" ]; then kernel_headers="/lib/modules/${kernel_version}/build" fi if [ ! -f "${kernel_headers}/Makefile" ]; then echo 'Missing kernel headers for given kernel!' exit 1 fi echo " * kernel headers: $kernel_headers" [ -n "$qcow_handler" ] && echo " * qcow2 handler: $qcow_handler" export _QCOW_HANDLER="$qcow_handler" if [ "$update" = "yes" ]; then pushd "${_repo_dir}" git pull popd fi echo 'Checking dracut...' if [ ! -f "${_dracut_dir}/dracut-install" ]; then echo "Dracut isn't available yet loading it." initialize_dracut fi for _dracut_module_dir in "${_repo_dir}/modules.d/"*; do [ -d "${_dracut_module_dir}" ] || continue _dracut_module="$(basename $_dracut_module_dir)" # TODO allow module-specific priority _dracut_module_target="${_dracut_dir}/modules.d/00${_dracut_module}" if [[ ! -L "$_dracut_module_target" || "$(readlink \ "$_dracut_module_target")" != "$_dracut_module_dir" ]]; then echo \ "Link ${_dracut_module} plugin into dracut modules folder" \ "($_dracut_module_dir -> $_dracut_module_target)." if ! ln --symbolic --force \ "$_dracut_module_dir" "$_dracut_module_target"; then echo \ "Linking \"$_dracut_module_dir\" to \"$_dracut_module_target\" failed." \ "We will copy them. So we have to recopy it every time to ensure that recompiled things take effect." cp --recursive --force --no-target-directory \ "$_dracut_module_dir" \ "$_dracut_module_target" fi fi done # default dracut modules _modules=(dnbd3-rootfs conf-tgz) echo "Default modules: ${_modules[@]}" if [ "$verbose" = "yes" ]; then dracut_parameters+=("--verbose") fi if [ "$debug" = "yes" ]; then dracut_parameters+=("--stdlog" "4") _modules+=(i18n terminfo) fi if [ "$use_systemd_in_initramfs" = "yes" ]; then _modules+=(systemd systemd-initrd dracut-systemd) fi # Initialize and exit if we are only preparing the build environment . "${_repo_dir}/modules.d/dnbd3-rootfs/helper/build.inc" build_initialize_components if [ "$initialize" = "yes" ]; then echo "Initialization succeeded." exit 0 fi echo 'Building initramfs...' "${_dracut_dir}/dracut.sh" --local \ $_loglevel --modules "${_modules[*]}" --conf /etc/dracut.conf \ --confdir /etc/dracut.conf.d "${dracut_parameter[@]}" \ --kver "${kernel_version}" "$file_path" _return_code=$? if [ "$_return_code" != 0 ]; then echo 'Building initramfs failed.' exit 1 fi # NOTE: dracut generate the initramfs with 0600 permissions chmod 0644 "${file_path}" } main "$@"