#!/usr/bin/env bash # -*- coding: utf-8 -*- # region header # Copyright Torben Sickert (info["~at~"]torben.website) 29.10.2015 # Janosch Dobler (TODO) 29.10.2015 # 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 # This tool provides a generic way to install systemd based remote linux # initramfs. # Examples # -------- # Start install progress command (Assuming internet is available): # >>> ./build_initramfs.sh # Note that you only get very necessary output until you provide "--verbose" as # commandline options. # Dependencies # ------------ # - bash (or any bash like shell) # - cpio - Copies files into or out of a cpio or tar archive. The # archive can be another file on the disk, a magnetic tape, # or a pipe. # - git - The stupid content tracker. # - test - Check file types and compare values (part of the shell). # - shift - Shifts the command line arguments (part of the shell). # - echo - Display a line of text (part of coreutils). # - mktemp - Create a temporary file or directory (part of coreutils). # - cat - Concatenate files and print on the standard output (part of # coreutils). # - rm - Remove files or directories (part of coreutils). # - sed - Stream editor for filtering and transforming text. # - gzip - Compress or expand files. # - curl - Transfer a URL # - tar - The GNU version of the tar archiving utility. # - make - GNU make utility to maintain groups of programs. # - cmake - The "cmake" executable is the CMake command-line interface. # - linux-headers - GNU make utility to maintain groups of programs. # - grep - Searches the named input files (or standard input if no # files are named, or if a single hyphen-minus (-) is given # as file name) for lines containing a match to the given # PATTERN. By default, grep prints the matching lines. ## region ensure presence of needed dependencies set -o errexit build_initramfs__needed_location="$(dirname "${BASH_SOURCE[0]}")/dnbd3-rootfs" if ! [[ -d "$build_initramfs__needed_location" ]]; then echo "The dnbd3 dracut plugin isn't available, loading it." if ! hash git 2>/dev/null; then echo "Needed dependency \"git\" isn't available. Please install \"git\" or provide the repositories data structure in \"$(dirname "${BASH_SOURCE[0]}")\"." result=1 fi build_initramfs__temporary_repository_location="$(mktemp --directory)" git clone git://git.openslx.org/openslx-ng/systemd-init.git \ "$build_initramfs__temporary_repository_location" pushd "$build_initramfs__temporary_repository_location" git submodule init git submodule update popd cp --recursive "${build_initramfs__temporary_repository_location}/builder/dnbd3-rootfs" \ "$build_initramfs__needed_location" rm --recursive --force "$build_initramfs__temporary_repository_location" fi set +o errexit ## endregion source "$(dirname "${BASH_SOURCE[0]}")/dnbd3-rootfs/scripts/rebash/core.sh" core.import exceptions exceptions.activate core.import logging core.import utils core.import change_root # endregion logging.set_commands_log_level debug logging.set_log_level critical # region properties build_initramfs_file_path='/boot/initramfs.img' build_initramfs_dracut_parameter='--force --no-hostonly' build_initramfs_verbose='no' build_initramfs_debug='no' build_initramfs_target='' build_initramfs_create_system_image='' build_initramfs_cleanup='no' build_initramfs_use_systemd_in_initramfs='no' build_initramfs_dependencies=(cpio git test shift mktemp cat rm sed gzip curl \ tar grep make gcc cmake readlink dirname dmsetup chroot qemu-nbd) # endregion # region functions ## region command line interface function build_initramfs_print_usage_message() { # Prints a description about how to use this program. logging.cat << EOF This program provides a generic way to install systemd based remote linux initramfs. EOF } function build_initramfs_print_usage_examples() { # Prints a description about how to use this program by providing examples. logging.cat << EOF Start install progress: >>> ./build_initramfs.sh EOF } function build_initramfs_print_command_line_option_description() { # Prints descriptions about each available command line option. logging.cat << EOF -h --help Shows this help message. -v --verbose Tells you what is going on (default: "$build_initramfs_verbose"). -d --debug Gives you any output from all tools which are used (default: "$build_initramfs_debug"). -p --file-path Target location for initramfs file (default: "$build_initramfs_file_path"). -c --cleanup Removes all distribution specific compiled files. -i --create-system-image Creates an image under given path from current system. (default: "$build_initramfs_create_system_image"). -t --target Creates an image against given target template filesystem. If not explicitly speicifed current system will be used as template system (default). -s --use-systemd-in-initramfs Use Systemd as init process in initramfs (improved performance but less features) (default: "$build_initramfs_use_systemd_in_initramfs"). Additional dracut parameter and normal parameter can be deleimiter via a single dash (-) (default: "$build_initramfs_dracut_parameter"). EOF } function build_initramfs_print_help_message() { # Provides a help message for this module. logging.plain "\nUsage: $0 [options]\n" build_initramfs_print_usage_message "$@" logging.plain '\nExamples:\n' build_initramfs_print_usage_examples "$@" logging.plain -e '\nOption descriptions:\n' build_initramfs_print_command_line_option_description "$@" logging.plain } function build_initramfs_parse_command_line() { # Provides the command line interface and interactive questions. while true; do case "$1" in -h|--help) shift build_initramfs_print_help_message "$0" exit 0 ;; -v|--verbose) shift build_initramfs_verbose='yes' ;; -d|--debug) shift build_initramfs_debug='yes' ;; -p|--file-path) shift build_initramfs_file_path="$1" if [[ "$build_initramfs_file_path" == '' ]]; then logging.critical \ "This options needs a path to save initramfs image to." return 1 fi shift ;; -c|--cleanup) shift build_initramfs_cleanup='yes' ;; -s|--use-systemd-in-initramfs) shift build_initramfs_use_systemd_in_initramfs='yes' ;; -i|--create-system-image) shift build_initramfs_create_system_image="$1" if [[ "$build_initramfs_create_system_image" == '' ]]; then logging.critical \ "This options needs a path to save image to." return 1 fi shift ;; -t|--target) shift build_initramfs_target="$1" if [[ "$build_initramfs_target" == '' ]]; then logging.critical "This options needs a path create initramfs from." return 1 fi shift ;; -) shift while [[ "$1" =~ ^.+$ ]]; do build_initramfs_dracut_parameter+=" $1" shift done shift ;; '') break ;; *) logging.critical "Given argument: \"$1\" is not available." return 1 esac done if [ "$build_initramfs_verbose" == 'yes' ]; then logging.set_commands_log_level debug logging.set_log_level info fi if [ "$build_initramfs_debug" == 'yes' ]; then logging.set_commands_log_level debug logging.set_log_level debug fi if [[ "$UID" != '0' ]]; then logging.critical \ "You have to run this script as \"root\" not as \"${USER}\"." exit 2 fi return 0 } ## endregion ## region helper function build_initramfs_initialize_dracut() { # Downloads and compiles dracut. # # Examples: # # >>> build_initramfs_initialize_dracut # ... mkdir --parents "$(dirname "${BASH_SOURCE[0]}")/dracut" logging.info 'Download and extract dracut.' curl --location \ https://www.kernel.org/pub/linux/utils/boot/dracut/dracut-043.tar.gz | \ tar --extract --gzip --directory \ "$(dirname "${BASH_SOURCE[0]}")/dracut" --strip-components 1 pushd "$(dirname "${BASH_SOURCE[0]}")/dracut" # NOTE: On virtualbox shared folder symlinks are not allowed. # NOTE: make the dracut-install binary (dracut-install resolves # dependencies etc.) logging.info 'Compiling dracut.' 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 cp "$(dirname "${BASH_SOURCE[0]}")/dracut/install/dracut-install" \ "$(dirname "${BASH_SOURCE[0]}")/dracut/dracut-install" popd return $? } function build_initramfs_create_qcow2_system() { # Packs current distribution in a qcow2 container. # # Examples: # # >>> build_initramfs_create_qcow2_system echo TODO # dmsetup snapshot ... #qemu-img create -f qcow2 "${CONTAINER_PATH}" "${QCOW_SIZE}" return $? } function build_initramfs_cleanup() { # Removes distribution specific generated files. # # Examples: # # >>> build_initramfs_cleanup local plugin_path="$(dirname "${BASH_SOURCE[0]}")/dnbd3-rootfs/" source "${plugin_path}module-setup.sh" moddir="$(cd "$plugin_path" &>/dev/null && pwd)" clean return $? } ## endregion # endregion # region controller ## region dependency checks utils.dependency_check "${build_initramfs_dependencies[*]}" # TODO check for existing kernel headers. # Find lib locations: $(gcc -print-prog-name=cc1plus) -v if ! ldconfig --print-cache | grep libz.so; then logging.critical "You have to install the compression library \"libz\". Otherwise we aren't able to compile dnbd3 for your kernel." exit 1 fi ## endregion if ! build_initramfs_parse_command_line "$@"; then build_initramfs_print_help_message "$0" exit 1 fi ## region handle delegated operations to specified target if [[ "$build_initramfs_target" != '' ]]; then build_initramfs__target="$build_initramfs_target" if [[ -f "$build_initramfs_target" ]]; then build_initramfs__target="$(mktemp --directory)" # TODO #qemu-nbd "$build_initramfs_target" "$build_initramfs__target" fi if [[ -d "$build_initramfs__target" ]]; then build_initramfs__temporary_working_directory="$(chroot \ "$build_initramfs__target" mktemp --directory)" mount --bind "$(pwd)" \ "${build_initramfs__target}${build_initramfs__temporary_working_directory}" build_initramfs__parameter_skip=false build_initramfs__parameter_to_forward=() for build_initramfs__parameter; do if $build_initramfs__parameter_skip; then build_initramfs__parameter_skip=false elif [[ "$build_initramfs__parameter" == '-t' ]] || \ [[ "$build_initramfs__parameter" == '--target' ]] then build_initramfs__parameter_skip=true else build_initramfs__parameter_to_forward+=("$build_initramfs__parameter") fi done # NOTE: We would have to temporary patch dracut to avoid removing the # environment variables "LD_LIBRARY_PATH" and "LD_PRELOAD" to get # "fakechroot" working with dracut. So we should remove this variables # before running the patched dracut version to follow the initial # intention. You should first do: # >>> unset LD_LIBRARY_PATH # >>> unset LD_PRELOAD # and patch "dracut.sh" temporary to comment out: # >>> unset LD_LIBRARY_PATH # >>> unset LD_PRELOAD # To avoid to broke the "fakechroot" environment pipe the ldconfig call # to the native one: # >>> FAKECHROOT_CMD_SUBST=/usr/bin/ldconfig=/usr/bin/ldconfig change_root "${build_initramfs__target}" \ "${build_initramfs__temporary_working_directory}/${BASH_SOURCE[0]}" \ ${build_initramfs__parameter_to_forward[*]} mv "${build_initramfs__target}/$build_initramfs_file_path" \ "$build_initramfs_file_path" fi exit 0 fi ## endregion ## region handle dependencies which can be resolved automatically logging.info 'Checking dracut.' if ! [[ -f "$(dirname "${BASH_SOURCE[0]}")/dracut/dracut-install" ]]; then logging.info "Dracut isn't available yet loading it." build_initramfs_initialize_dracut fi build_initramfs__dracut_modules_source='../../dnbd3-rootfs' build_initramfs__dracut_modules_target="$(dirname "${BASH_SOURCE[0]}")/dracut/modules.d/90dnbd3-rootfs" if [[ ! -L "$build_initramfs__dracut_modules_target" || "$(readlink \ "$build_initramfs__dracut_modules_target")" != \ "$build_initramfs__dracut_modules_source" ]] then logging.info \ "Link dnbd3 plugin into dracut modules folder ($build_initramfs__dracut_modules_source -> $build_initramfs__dracut_modules_target)." if ! ln --symbolic --force "$build_initramfs__dracut_modules_source" \ "$build_initramfs__dracut_modules_target" 2>/dev/null; then logging.warn \ "Link \"$build_initramfs__dracut_modules_source\" to \"$build_initramfs__dracut_modules_target\" fails. 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 \ "$(dirname "${BASH_SOURCE[0]}")/$(basename "$build_initramfs__dracut_modules_source")" \ "$build_initramfs__dracut_modules_target" fi fi ## endregion ## region prepare and perform final dracut call build_initramfs__loglevel='' if [ "$build_initramfs_verbose" == 'yes' ]; then build_initramfs__loglevel='--verbose' fi build_initramfs__modules='dnbd3-rootfs' if [ "$build_initramfs_debug" == 'yes' ]; then build_initramfs__loglevel="$build_initramfs__loglevel --stdlog 4" build_initramfs__modules="$build_initramfs__modules i18n terminfo" fi if [ "$build_initramfs_use_systemd_in_initramfs" == 'yes' ]; then build_initramfs__modules="$build_initramfs__modules systemd systemd-initrd dracut-systemd" fi if [[ "$build_initramfs_create_system_image" != '' ]]; then logging.info 'Create system image.' build_initramfs_create_qcow2_system "$build_initramfs_create_system_image" elif [[ "$build_initramfs_cleanup" == 'yes' ]]; then logging.info 'Removing distribution specific files.' build_initramfs_cleanup else logging.info 'Build initramfs.' # NOTE: We deactivate our exception handle since dracut returns "1" if it # is launched with help parameter ("-h" or "--help"). exceptions.deactivate "$(dirname "${BASH_SOURCE[0]}")/dracut/dracut.sh" --local \ $build_initramfs__loglevel --modules "$build_initramfs__modules" \ ${build_initramfs_dracut_parameter[*]} "$build_initramfs_file_path" exceptions.activate fi exceptions.deactivate ## endregion # endregion # region vim modline # vim: set tabstop=4 shiftwidth=4 expandtab: # vim: foldmethod=marker foldmarker=region,endregion: # endregion