blob: e41c2293b19b5ac007feaa5a9f750f971d6eb12b (
plain) (
tree)
|
|
#!/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
# 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.
## region ensure presence of needed dependencies
set -o errexit
_needed_location="$(dirname "${BASH_SOURCE[0]}")/dnbd3-rootfs"
if ! [[ -d "$_needed_location" ]]; then
echo "The dnbd3 dracut plugin isn't available, loading it."
if ! hash git; then
echo "Needed dependency \"git\" isn't available. Please install \"git\" or provide the repositories data structure in \"$(dirname "${BASH_SOURCE[0]}")\"."
fi
_temporary_repository_location="$(mktemp --directory)"
git clone --depth 1 git://git.openslx.org/openslx-ng/systemd-init.git \
"$_temporary_repository_location"
pushd "$_temporary_repository_location"
git submodule update --init --depth 1
popd
cp --recursive \
"${_temporary_repository_location}/builder/dnbd3-rootfs" \
"$_needed_location"
rm --recursive --force "$_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
# region properties
dracut_resource_url='https://www.kernel.org/pub/linux/utils/boot/dracut/dracut-043.tar.gz'
file_path='/boot/initramfs.img'
dracut_parameter='--force --no-hostonly'
verbose='no'
debug='no'
target=''
create_system_image=''
cleanup='no'
full_cleanup='no'
use_systemd_in_initramfs='no'
# TODO check for presence of linux kernel headers as core dependency
declare -A core_dependencies=(
[cat]='print messages' \
[cpio]='pack initramfs' \
[dhclient]='support network connection in resulting initramfs' \
[dirname]='core logic' \
[dmsetup]='create a (temporary) writable layer during boot' \
[grep]='retrieve right boot partition during boot' \
[mktemp]='create save temporary files and dictionaries' \
[readlink]="connect dracut module with dracut's module system" \
[rm]='remove (temporary) files' \
[shift]='parse command line' \
[sed]='process strings' \
['cmake gcc make']='dynamically compile needed resources against current or given kernel')
declare -A optional_dependencies=(
[chroot]='build against a distribution other than this program runs in' \
['curl git gzip tar']='dynamically retrieve and unpack missing application which will be compiled for current or given kernel' \
[qemu-img]='support template systems in container (usually used by virtual runtime environments')
declare -A core_shared_library_pattern_dependencies=(
[libz]='compile dnbd3 for given or current kernel')
declare -A optional_shared_library_pattern_dependencies=()
declare -A core_package_dependencies=()
declare -A optional_package_dependencies=(
['fuse glib-2.0 pixman-1']='support template systems in container (usually used by virtual runtime environments)')
# endregion
# region functions
## region command line interface
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
}
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
}
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: "$verbose").
-d --debug Gives you any output from all tools which are used
(default: "$debug").
-p --file-path Target location for initramfs file
(default: "$file_path").
-c --cleanup Removes all distribution specific compiled files.
-f --full-cleanup Removes all retrieved and compiled files (usefull to retrieve latest version of all nested modules).
-i --create-system-image Creates an image under given path from current system.
(default: "$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: "$target").
-s --use-systemd-in-initramfs Use Systemd as init process in initramfs
(improved performance but less features)
(default: "$use_systemd_in_initramfs").
Additional dracut parameter and normal parameter can be added by delimiting
them via a single dash (-) (default: "$dracut_parameter" with
"--modules" and dynamically determined modules. Additional custom modules are
concated.).
EOF
}
print_help_message() {
# Provides a help message for this module.
logging.plain "\nUsage: $0 [options]\n"
print_usage_message "$@"
logging.plain '\nExamples:\n'
print_usage_examples "$@"
logging.plain -e '\nOption descriptions:\n'
print_command_line_option_description "$@"
logging.plain
}
parse_command_line() {
# Provides the command line interface and interactive questions.
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)
shift
file_path="$1"
if [[ "$file_path" == '' ]]; then
logging.critical \
"This options 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'
;;
-i|--create-system-image)
shift
create_system_image="$1"
if [[ "$create_system_image" == '' ]]; then
logging.critical \
"This options needs a path to save image to."
return 1
fi
shift
;;
-t|--target)
shift
target="$1"
if [[ "$target" == '' ]]; then
logging.critical \
"This options needs a path create initramfs from."
return 1
fi
shift
;;
-)
shift
while [[ "$1" =~ ^.+$ ]]; do
dracut_parameter+=" $1"
shift
done
shift
;;
'')
break
;;
*)
logging.critical "Given argument: \"$1\" is not available."
return 1
esac
done
if [ "$verbose" == 'yes' ]; then
logging.set_level info
fi
if [ "$debug" == 'yes' ]; then
logging.set_level debug
fi
return 0
# TODO This sanity check is only needed for some cli combinations.
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
dependency_check() {
# Check for given dependencies with given dependency checker and log
# corresponding messages.
local result=0
eval 'for dependency_group in "${!'$1'_'$2'[@]}"; do
# NOTE: If "dependency_check_result" would be marked as local it is
# empty later.
dependency_check_result="$($3 $dependency_group)" || \
local return_code=$?
if [[ $return_code == 1 ]]; then
echo "$dependency_check_result"
return $return_code
elif [[ $return_code == 2 ]]; then
while read dependency; do
eval "local reason=\${${1}_${2}[\"\$dependency_group\"]}"
local message="Missing $1 $4 dependency \"$dependency\" needed to $reason."
if [[ $1 == core ]]; then
logging.critical "$message"
else
logging.warn "$message"
fi
done <<< $dependency_check_result
result=2
fi
return_code=0
done'
return $result
}
initialize_dracut() {
# Downloads and compiles dracut.
#
# Examples:
#
# >>> initialize_dracut
# ...
if [[ ! -f "$(dirname "${BASH_SOURCE[0]}")/dracut/install/dracut-install" ]]
then
mkdir --parents "$(dirname "${BASH_SOURCE[0]}")/dracut"
logging.info 'Download and extract dracut.'
curl --location "$dracut_resource_url" | 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
popd
fi
cp "$(dirname "${BASH_SOURCE[0]}")/dracut/install/dracut-install" \
"$(dirname "${BASH_SOURCE[0]}")/dracut/dracut-install"
return $?
}
cleanup() {
# Removes distribution specific generated files.
#
# Examples:
#
# >>> 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
result=0
dependency_check core dependencies utils_dependency_check program || \
result=$?
dependency_check core shared_library_pattern_dependencies \
utils_dependency_check_shared_library 'shared library' || result=$?
dependency_check core package_dependencies utils_dependency_check_pkgconfig \
package || result=$?
[[ $result == 0 ]] || exit $result
logging.set_commands_level debug
logging.set_level critical
if ! parse_command_line "$@"; then
print_help_message "$0"
exit 1
fi
dependency_check optional dependencies utils_dependency_check program || \
result=$?
dependency_check optional shared_library_pattern_dependencies \
utils_dependency_check_shared_library 'shared library' || result=$?
dependency_check optional package_dependencies \
utils_dependency_check_pkgconfig package || result=$?
[[ $result == 1 ]] && exit $result
## region handle delegated operations to specified target
if [[ "$target" != '' ]]; then
_target="$target"
if [[ -f "$target" ]]; then
_target="$(mktemp --directory)"
_xmount_mountpoint_target="$(mktemp --directory)"
xmount --in qemu "$target" --out raw \
"$_xmount_mountpoint_target"
_xmount_device_target="$(losetup --find)"
losetup "$_xmount_device_target" loopdev \
"${_xmount_mountpoint_target}/"*.dd
mount "$_xmount_device_target" \
"$_target"
fi
if [[ -d "$_target" ]]; then
_temporary_working_directory="$(chroot "$_target" mktemp --directory)"
mount --bind "$(pwd)" "${_target}${_temporary_working_directory}"
_parameter_skip=false
_parameter_to_forward=()
for _parameter; do
if $_parameter_skip; then
_parameter_skip=false
elif [[ "$_parameter" == '-t' ]] || \
[[ "$_parameter" == '--target' ]]
then
_parameter_skip=true
else
_parameter_to_forward+=("$_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 "${_target}" \
"${_temporary_working_directory}/${BASH_SOURCE[0]}" \
${_parameter_to_forward[*]}
mv "${_target}/$file_path" "$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."
initialize_dracut
fi
_dracut_modules_source='../../dnbd3-rootfs'
_dracut_modules_target="$(dirname "${BASH_SOURCE[0]}")/dracut/modules.d/90dnbd3-rootfs"
if [[ ! -L "$_dracut_modules_target" || "$(readlink \
"$_dracut_modules_target")" != "$_dracut_modules_source" ]]
then
logging.info \
"Link dnbd3 plugin into dracut modules folder ($_dracut_modules_source -> $_dracut_modules_target)."
if ! ln --symbolic --force "$_dracut_modules_source" \
"$_dracut_modules_target" 2>/dev/null; then
logging.warn \
"Link \"$_dracut_modules_source\" to \"$_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 "$_dracut_modules_source")" \
"$_dracut_modules_target"
fi
fi
## endregion
## region prepare and perform final dracut call
_loglevel=''
if [ "$verbose" == 'yes' ]; then
_loglevel='--verbose'
fi
_modules='dnbd3-rootfs'
if [ "$debug" == 'yes' ]; then
_loglevel="$_loglevel --stdlog 4"
_modules="$_modules i18n terminfo"
fi
if [ "$use_systemd_in_initramfs" == 'yes' ]; then
_modules="$_modules systemd systemd-initrd dracut-systemd"
fi
if [[ "$create_system_image" != '' ]]; then
logging.info 'Create system image.'
create_qcow2_system "$create_system_image"
elif [[ "$full_cleanup" == 'yes' ]]; then
logging.info 'Removing all modules.'
rm "$(dirname "${BASH_SOURCE[0]}")/dnbd3-rootfs" \
"$(dirname "${BASH_SOURCE[0]}")/dracut" --recursive --force
elif [[ "$cleanup" == 'yes' ]]; then
logging.info 'Removing distribution specific files.'
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
if ! "$(dirname "${BASH_SOURCE[0]}")/dracut/dracut.sh" --local \
$_loglevel --modules "$_modules" ${dracut_parameter[*]} "$file_path"
then
logging.error 'Building initial ram file system fails.'
exit 1
fi
exceptions.activate
fi
exceptions.deactivate
## endregion
# endregion
# region vim modline
# vim: set tabstop=4 shiftwidth=4 expandtab:
# vim: foldmethod=marker foldmarker=region,endregion:
# endregion
|